25 KiB
Δημιουργία Εφαρμογής Τραπεζικής Μέρος 4: Έννοιες Διαχείρισης Κατάστασης
Κουίζ Πριν το Μάθημα
Εισαγωγή
Καθώς μια διαδικτυακή εφαρμογή μεγαλώνει, γίνεται πρόκληση να παρακολουθείς όλες τις ροές δεδομένων. Ποιος κώδικας λαμβάνει τα δεδομένα, ποια σελίδα τα χρησιμοποιεί, πού και πότε πρέπει να ενημερωθούν... είναι εύκολο να καταλήξεις με μπερδεμένο κώδικα που είναι δύσκολο να συντηρηθεί. Αυτό ισχύει ιδιαίτερα όταν χρειάζεται να μοιραστείς δεδομένα μεταξύ διαφορετικών σελίδων της εφαρμογής σου, όπως τα δεδομένα χρήστη. Η έννοια της διαχείρισης κατάστασης υπήρχε πάντα σε κάθε είδους προγράμματα, αλλά καθώς οι διαδικτυακές εφαρμογές γίνονται όλο και πιο περίπλοκες, είναι πλέον ένα βασικό σημείο που πρέπει να λαμβάνεται υπόψη κατά την ανάπτυξη.
Σε αυτό το τελευταίο μέρος, θα επανεξετάσουμε την εφαρμογή που δημιουργήσαμε για να αναθεωρήσουμε πώς διαχειρίζεται η κατάσταση, επιτρέποντας την υποστήριξη ανανέωσης του προγράμματος περιήγησης οποιαδήποτε στιγμή και τη διατήρηση δεδομένων μεταξύ των συνεδριών χρήστη.
Προαπαιτούμενα
Πρέπει να έχεις ολοκληρώσει το μέρος ανάκτησης δεδομένων της διαδικτυακής εφαρμογής για αυτό το μάθημα. Πρέπει επίσης να εγκαταστήσεις το Node.js και να εκτελέσεις το API διακομιστή τοπικά ώστε να μπορείς να διαχειριστείς τα δεδομένα λογαριασμού.
Μπορείς να ελέγξεις ότι ο διακομιστής λειτουργεί σωστά εκτελώντας αυτήν την εντολή σε ένα τερματικό:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
Αναθεώρηση της διαχείρισης κατάστασης
Στο προηγούμενο μάθημα, εισαγάγαμε μια βασική έννοια κατάστασης στην εφαρμογή μας με τη χρήση της παγκόσμιας μεταβλητής account, η οποία περιέχει τα τραπεζικά δεδομένα του χρήστη που είναι συνδεδεμένος. Ωστόσο, η τρέχουσα υλοποίησή μας έχει κάποια προβλήματα. Δοκίμασε να ανανεώσεις τη σελίδα όταν βρίσκεσαι στον πίνακα ελέγχου. Τι συμβαίνει;
Υπάρχουν 3 προβλήματα με τον τρέχοντα κώδικα:
- Η κατάσταση δεν διατηρείται, καθώς μια ανανέωση του προγράμματος περιήγησης σε επιστρέφει στη σελίδα σύνδεσης.
- Υπάρχουν πολλές συναρτήσεις που τροποποιούν την κατάσταση. Καθώς η εφαρμογή μεγαλώνει, αυτό μπορεί να δυσκολέψει την παρακολούθηση των αλλαγών και είναι εύκολο να ξεχάσεις να ενημερώσεις κάτι.
- Η κατάσταση δεν καθαρίζεται, οπότε όταν κάνεις κλικ στο Αποσύνδεση, τα δεδομένα του λογαριασμού παραμένουν, παρόλο που βρίσκεσαι στη σελίδα σύνδεσης.
Θα μπορούσαμε να ενημερώσουμε τον κώδικά μας για να αντιμετωπίσουμε αυτά τα προβλήματα ένα προς ένα, αλλά αυτό θα δημιουργούσε περισσότερη επανάληψη κώδικα και θα έκανε την εφαρμογή πιο περίπλοκη και δύσκολη στη συντήρηση. Ή θα μπορούσαμε να σταματήσουμε για λίγα λεπτά και να αναθεωρήσουμε τη στρατηγική μας.
Ποια προβλήματα προσπαθούμε πραγματικά να λύσουμε εδώ;
Η διαχείριση κατάστασης αφορά την εύρεση μιας καλής προσέγγισης για την επίλυση αυτών των δύο συγκεκριμένων προβλημάτων:
- Πώς να διατηρήσουμε τις ροές δεδομένων σε μια εφαρμογή κατανοητές;
- Πώς να διατηρήσουμε τα δεδομένα κατάστασης πάντα συγχρονισμένα με τη διεπαφή χρήστη (και το αντίστροφο);
Αφού αντιμετωπίσεις αυτά τα ζητήματα, οποιαδήποτε άλλα προβλήματα μπορεί να έχουν ήδη λυθεί ή να έχουν γίνει ευκολότερα να λυθούν. Υπάρχουν πολλές πιθανές προσεγγίσεις για την επίλυση αυτών των προβλημάτων, αλλά θα ακολουθήσουμε μια κοινή λύση που συνίσταται στο να κεντροποιήσουμε τα δεδομένα και τους τρόπους αλλαγής τους. Οι ροές δεδομένων θα λειτουργούν ως εξής:
Δεν θα καλύψουμε εδώ το μέρος όπου τα δεδομένα ενεργοποιούν αυτόματα την ενημέρωση της προβολής, καθώς αυτό συνδέεται με πιο προχωρημένες έννοιες του Αντιδραστικού Προγραμματισμού. Είναι ένα καλό θέμα για περαιτέρω εμβάθυνση αν ενδιαφέρεσαι.
✅ Υπάρχουν πολλές βιβλιοθήκες εκεί έξω με διαφορετικές προσεγγίσεις στη διαχείριση κατάστασης, με το Redux να είναι μια δημοφιλής επιλογή. Ρίξε μια ματιά στις έννοιες και τα μοτίβα που χρησιμοποιούνται, καθώς συχνά είναι ένας καλός τρόπος να μάθεις ποια πιθανά προβλήματα μπορεί να αντιμετωπίσεις σε μεγάλες διαδικτυακές εφαρμογές και πώς μπορούν να λυθούν.
Εργασία
Θα ξεκινήσουμε με λίγη αναδιάρθρωση. Αντικατάστησε τη δήλωση account:
let account = null;
Με:
let state = {
account: null
};
Η ιδέα είναι να κεντροποιήσουμε όλα τα δεδομένα της εφαρμογής μας σε ένα μόνο αντικείμενο κατάστασης. Προς το παρόν έχουμε μόνο το account στην κατάσταση, οπότε δεν αλλάζει πολλά, αλλά δημιουργεί μια βάση για μελλοντικές εξελίξεις.
Πρέπει επίσης να ενημερώσουμε τις συναρτήσεις που το χρησιμοποιούν. Στις συναρτήσεις register() και login(), αντικατάστησε το account = ... με state.account = ....
Στην αρχή της συνάρτησης updateDashboard(), πρόσθεσε αυτή τη γραμμή:
const account = state.account;
Αυτή η αναδιάρθρωση από μόνη της δεν έφερε πολλές βελτιώσεις, αλλά η ιδέα ήταν να θέσουμε τα θεμέλια για τις επόμενες αλλαγές.
Παρακολούθηση αλλαγών δεδομένων
Τώρα που έχουμε θέσει το αντικείμενο state για την αποθήκευση των δεδομένων μας, το επόμενο βήμα είναι να κεντροποιήσουμε τις ενημερώσεις. Στόχος είναι να γίνει ευκολότερη η παρακολούθηση οποιωνδήποτε αλλαγών και πότε αυτές συμβαίνουν.
Για να αποφύγουμε αλλαγές στο αντικείμενο state, είναι επίσης καλή πρακτική να το θεωρούμε αμετάβλητο, που σημαίνει ότι δεν μπορεί να τροποποιηθεί καθόλου. Αυτό σημαίνει επίσης ότι πρέπει να δημιουργήσεις ένα νέο αντικείμενο κατάστασης αν θέλεις να αλλάξεις κάτι σε αυτό. Με αυτόν τον τρόπο, δημιουργείς μια προστασία από πιθανές ανεπιθύμητες παρενέργειες και ανοίγεις δυνατότητες για νέες λειτουργίες στην εφαρμογή σου, όπως η υλοποίηση αναίρεσης/επανάληψης, ενώ ταυτόχρονα διευκολύνεις την αποσφαλμάτωση. Για παράδειγμα, θα μπορούσες να καταγράψεις κάθε αλλαγή που γίνεται στην κατάσταση και να διατηρήσεις ένα ιστορικό των αλλαγών για να κατανοήσεις την πηγή ενός σφάλματος.
Στην JavaScript, μπορείς να χρησιμοποιήσεις το Object.freeze() για να δημιουργήσεις μια αμετάβλητη έκδοση ενός αντικειμένου. Αν προσπαθήσεις να κάνεις αλλαγές σε ένα αμετάβλητο αντικείμενο, θα προκληθεί εξαίρεση.
✅ Γνωρίζεις τη διαφορά μεταξύ ενός ρηχού και ενός βαθιού αμετάβλητου αντικειμένου; Μπορείς να διαβάσεις σχετικά εδώ.
Εργασία
Ας δημιουργήσουμε μια νέα συνάρτηση updateState():
function updateState(property, newData) {
state = Object.freeze({
...state,
[property]: newData
});
}
Σε αυτή τη συνάρτηση, δημιουργούμε ένα νέο αντικείμενο κατάστασης και αντιγράφουμε δεδομένα από την προηγούμενη κατάσταση χρησιμοποιώντας τον τελεστή εξάπλωσης (...). Στη συνέχεια, αντικαθιστούμε μια συγκεκριμένη ιδιότητα του αντικειμένου κατάστασης με τα νέα δεδομένα χρησιμοποιώντας τη σημειογραφία αγκύλης [property] για την ανάθεση. Τέλος, κλειδώνουμε το αντικείμενο για να αποτρέψουμε τροποποιήσεις χρησιμοποιώντας το Object.freeze(). Προς το παρόν, έχουμε μόνο την ιδιότητα account αποθηκευμένη στην κατάσταση, αλλά με αυτή την προσέγγιση μπορείς να προσθέσεις όσες ιδιότητες χρειάζεσαι.
Θα ενημερώσουμε επίσης την αρχικοποίηση του state για να διασφαλίσουμε ότι η αρχική κατάσταση είναι επίσης κλειδωμένη:
let state = Object.freeze({
account: null
});
Μετά από αυτό, ενημέρωσε τη συνάρτηση register αντικαθιστώντας την ανάθεση state.account = result; με:
updateState('account', result);
Κάνε το ίδιο με τη συνάρτηση login, αντικαθιστώντας το state.account = data; με:
updateState('account', data);
Τώρα θα εκμεταλλευτούμε την ευκαιρία να διορθώσουμε το πρόβλημα των δεδομένων λογαριασμού που δεν διαγράφονται όταν ο χρήστης κάνει κλικ στο Αποσύνδεση.
Δημιούργησε μια νέα συνάρτηση logout():
function logout() {
updateState('account', null);
navigate('/login');
}
Στη συνάρτηση updateDashboard(), αντικατάστησε την ανακατεύθυνση return navigate('/login'); με return logout();.
Δοκίμασε να εγγραφείς με νέο λογαριασμό, να αποσυνδεθείς και να συνδεθείς ξανά για να ελέγξεις ότι όλα λειτουργούν σωστά.
Συμβουλή: μπορείς να δεις όλες τις αλλαγές κατάστασης προσθέτοντας
console.log(state)στο τέλος τηςupdateState()και ανοίγοντας την κονσόλα στα εργαλεία ανάπτυξης του προγράμματος περιήγησης.
Διατήρηση της κατάστασης
Οι περισσότερες διαδικτυακές εφαρμογές χρειάζονται να διατηρούν δεδομένα για να λειτουργούν σωστά. Όλα τα κρίσιμα δεδομένα συνήθως αποθηκεύονται σε μια βάση δεδομένων και είναι προσβάσιμα μέσω ενός API διακομιστή, όπως τα δεδομένα λογαριασμού χρήστη στην περίπτωσή μας. Αλλά μερικές φορές, είναι επίσης ενδιαφέρον να διατηρείς κάποια δεδομένα στην εφαρμογή πελάτη που εκτελείται στο πρόγραμμα περιήγησης, για καλύτερη εμπειρία χρήστη ή για βελτίωση της απόδοσης φόρτωσης.
Όταν θέλεις να διατηρήσεις δεδομένα στο πρόγραμμα περιήγησης, υπάρχουν μερικές σημαντικές ερωτήσεις που πρέπει να κάνεις:
- Είναι τα δεδομένα ευαίσθητα; Πρέπει να αποφεύγεις την αποθήκευση οποιωνδήποτε ευαίσθητων δεδομένων στον πελάτη, όπως κωδικούς πρόσβασης χρηστών.
- Για πόσο καιρό χρειάζεσαι να διατηρήσεις αυτά τα δεδομένα; Σκοπεύεις να έχεις πρόσβαση σε αυτά τα δεδομένα μόνο για την τρέχουσα συνεδρία ή θέλεις να αποθηκευτούν για πάντα;
Υπάρχουν πολλοί τρόποι αποθήκευσης πληροφοριών μέσα σε μια διαδικτυακή εφαρμογή, ανάλογα με το τι θέλεις να επιτύχεις. Για παράδειγμα, μπορείς να χρησιμοποιήσεις τις διευθύνσεις URL για να αποθηκεύσεις ένα ερώτημα αναζήτησης και να το κάνεις κοινόχρηστο μεταξύ χρηστών. Μπορείς επίσης να χρησιμοποιήσεις HTTP cookies αν τα δεδομένα χρειάζεται να μοιραστούν με τον διακομιστή, όπως πληροφορίες αυθεντικοποίησης.
Μια άλλη επιλογή είναι να χρησιμοποιήσεις μία από τις πολλές APIs του προγράμματος περιήγησης για την αποθήκευση δεδομένων. Δύο από αυτές είναι ιδιαίτερα ενδιαφέρουσες:
localStorage: ένα Κατάστημα Κλειδιού/Τιμής που επιτρέπει τη διατήρηση δεδομένων συγκεκριμένων για τον τρέχοντα ιστότοπο μεταξύ διαφορετικών συνεδριών. Τα δεδομένα που αποθηκεύονται σε αυτό δεν λήγουν ποτέ.sessionStorage: λειτουργεί το ίδιο με τοlocalStorage, εκτός από το ότι τα δεδομένα που αποθηκεύονται σε αυτό διαγράφονται όταν τελειώνει η συνεδρία (όταν κλείνει το πρόγραμμα περιήγησης).
Σημείωσε ότι και οι δύο αυτές APIs επιτρέπουν μόνο την αποθήκευση συμβολοσειρών. Αν θέλεις να αποθηκεύσεις σύνθετα αντικείμενα, θα χρειαστεί να τα μετατρέψεις σε μορφή JSON χρησιμοποιώντας το JSON.stringify().
✅ Αν θέλεις να δημιουργήσεις μια διαδικτυακή εφαρμογή που δεν λειτουργεί με διακομιστή, είναι επίσης δυνατό να δημιουργήσεις μια βάση δεδομένων στον πελάτη χρησιμοποιώντας το IndexedDB API. Αυτό προορίζεται για προχωρημένες περιπτώσεις χρήσης ή αν χρειάζεσαι να αποθηκεύσεις σημαντική ποσότητα δεδομένων, καθώς είναι πιο περίπλοκο στη χρήση.
Εργασία
Θέλουμε οι χρήστες μας να παραμένουν συνδεδεμένοι μέχρι να κάνουν ρητά κλικ στο κουμπί Αποσύνδεση, οπότε θα χρησιμοποιήσουμε το localStorage για να αποθηκεύσουμε τα δεδομένα λογαριασμού. Πρώτα, ας ορίσουμε ένα κλειδί που θα χρησιμοποιήσουμε για την αποθήκευση των δεδομένων μας.
const storageKey = 'savedAccount';
Στη συνέχεια, πρόσθεσε αυτή τη γραμμή στο τέλος της συνάρτησης updateState():
localStorage.setItem(storageKey, JSON.stringify(state.account));
Με αυτό, τα δεδομένα λογαριασμού χρήστη θα διατηρούνται και θα είναι πάντα ενημερωμένα, καθώς προηγουμένως κεντροποιήσαμε όλες τις ενημερώσεις κατάστασης. Εδώ αρχίζουμε να επωφελούμαστε από όλες τις προηγούμενες αναδιαρθρώσεις μας 🙂.
Καθώς τα δεδομένα αποθηκεύονται, πρέπει επίσης να φροντίσουμε για την επαναφορά τους όταν φορτώνεται η εφαρμογή. Δεδομένου ότι θα αρχίσουμε να έχουμε περισσότερο κώδικα αρχικοποίησης, μπορεί να είναι καλή ιδέα να δημιουργήσουμε μια νέα συνάρτηση init, η οποία θα περιλαμβάνει και τον προηγούμενο κώδικα στο τέλος του app.js:
function init() {
const savedAccount = localStorage.getItem(storageKey);
if (savedAccount) {
updateState('account', JSON.parse(savedAccount));
}
// Our previous initialization code
window.onpopstate = () => updateRoute();
updateRoute();
}
init();
Εδώ ανακτούμε τα αποθηκευμένα δεδομένα και, αν υπάρχουν, ενημερώνουμε την κατάσταση ανάλογα. Είναι σημαντικό να το κάνουμε αυτό πριν ενημερώσουμε τη διαδρομή, καθώς μπορεί να υπάρχει κώδικας που βασίζεται στην κατάσταση κατά την ενημέρωση της σελίδας.
Μπορούμε επίσης να κάνουμε τη σελίδα Πίνακας Ελέγχου την προεπιλεγμένη σελίδα της εφαρμογής μας, καθώς τώρα διατηρούμε τα δεδομένα λογαριασμού. Αν δεν βρεθούν δεδομένα, ο πίνακας ελέγχου φροντίζει να ανακατευθύνει στη σελίδα Σύνδεση ούτως ή άλλως. Στη συνάρτηση updateRoute(), αντικατάστησε την προεπιλεγμένη return navigate('/login'); με `
Κουίζ μετά τη διάλεξη
Εργασία
Υλοποιήστε το παράθυρο διαλόγου "Προσθήκη συναλλαγής"
Ακολουθεί ένα παράδειγμα αποτελέσματος μετά την ολοκλήρωση της εργασίας:
Αποποίηση ευθύνης:
Αυτό το έγγραφο έχει μεταφραστεί χρησιμοποιώντας την υπηρεσία αυτόματης μετάφρασης Co-op Translator. Παρόλο που καταβάλλουμε προσπάθειες για ακρίβεια, παρακαλούμε να έχετε υπόψη ότι οι αυτόματες μεταφράσεις ενδέχεται να περιέχουν σφάλματα ή ανακρίβειες. Το πρωτότυπο έγγραφο στη μητρική του γλώσσα θα πρέπει να θεωρείται η αυθεντική πηγή. Για κρίσιμες πληροφορίες, συνιστάται επαγγελματική ανθρώπινη μετάφραση. Δεν φέρουμε ευθύνη για τυχόν παρεξηγήσεις ή εσφαλμένες ερμηνείες που προκύπτουν από τη χρήση αυτής της μετάφρασης.

