Wenn Sie Ihre Programmierkarriere beginnen, kann es ein wenig beängstigend sein, im Quellcode offener Bibliotheken und Frameworks zu stöbern. In diesem Artikel teilt Karl Mungazi seine Erfahrungen mit, wie er seine Angst überwunden hat, und begann, den Quellcode zu verwenden, um Wissen zu erwerben und Fähigkeiten zu entwickeln. Er verwendet Redux auch, um zu zeigen, wie er die Bibliothek „analysiert“.Erinnerst du dich, als du zum ersten Mal in den Code einer Bibliothek oder eines Frameworks eingetaucht bist, die du oft verwendest? In meinem Leben kam dieser Moment bei meinem ersten Job als Front-End-Entwickler vor drei Jahren.
Wir haben gerade ein veraltetes proprietäres Framework neu geschrieben, mit dem interaktive Schulungskurse erstellt wurden. Zu Beginn der Umschreibungsarbeit haben wir uns einige schlüsselfertige Lösungen angesehen, darunter Mithril, Inferno, Angular, React, Aurelia, Vue und Polymer. Da ich noch ein junger Padawan war (der gerade vom Journalismus zur Webentwicklung gewechselt war), hatte ich schreckliche Angst vor der Komplexität der einzelnen Frameworks und dem Unverständnis darüber, wie sie funktionieren.
Das Verständnis begann zu kommen, als ich begann, das Mithril-Gerüst sorgfältig zu erforschen. Seitdem wurden meine Kenntnisse in JavaScript - und in der Programmierung im Allgemeinen - dank der Stunden, die ich in den Interna der Bibliothek verbracht habe, die ich täglich bei der Arbeit und in meinen eigenen Projekten verwendet habe, erheblich vertieft. In diesem Artikel werde ich Ihnen erklären, wie Sie Ihre Lieblingsbibliothek als Tutorial verwenden können
Ich begann den Quellcode mit der Hyperscript-Funktion von Mithril zu lesenVorteile des Parsens von Quellcode
Einer der Hauptvorteile beim Parsen von Quellcode besteht darin, dass Sie viel lernen können. Als ich anfing, den Mithril-Code zu analysieren, hatte ich eine sehr schlechte Vorstellung davon, was das virtuelle DOM war. Als ich fertig war, wusste ich bereits, dass das virtuelle DOM eine Technik ist, bei der ein Baum von Objekten erstellt wird, die die Benutzeroberfläche beschreiben. Dieser Baum kann dann mithilfe einer DOM-API wie document.createElement in DOM-Elemente konvertiert werden. Zum Aktualisieren wird ein neuer Baum erstellt, der den zukünftigen Status der Schnittstelle beschreibt und dann mit der vorherigen Version dieses Baums verglichen wird.
Ich habe darüber in vielen Artikeln und Handbüchern gelesen, aber am lehrreichsten war es, dies alles zu beobachten, während ich an unserer Anwendung arbeitete. Ich habe auch gelernt, beim Vergleich von Frameworks die richtigen Fragen zu stellen. Anstatt beispielsweise Bewertungen zu vergleichen, können Sie die Frage stellen: "Wie wirkt sich die Funktionsweise dieses Frameworks auf Änderungen auf die Leistung und den Komfort des Endbenutzers aus?"
Ein weiteres Plus ist die Entwicklung eines Verständnisses für eine gute Anwendungsarchitektur. Trotz der Tatsache, dass die meisten Open-Source-Projekte im Allgemeinen mehr oder weniger ähnlich aufgebaut sind wie ihre Repositories, weisen sie immer noch Unterschiede auf. Die Struktur von Mithril ist sehr flach. Wenn Sie mit der API vertraut sind, können Sie durchaus realistische Annahmen über den Code in den Ordnern Rendering, Router und Request treffen. Die Struktur von React spiegelt andererseits die neue Architektur wider. Die Entwickler trennten das Modul, das für die Aktualisierung der Benutzeroberfläche (React-Reconciler) verantwortlich ist, von dem Modul, das für das Rendern der DOM-Elemente (React-Dom) verantwortlich ist.
Einer der Vorteile dieser Trennung für Entwickler besteht darin, dass sie ihre
eigenen Renderer mithilfe von Hooks im React-Reconciler schreiben können. Parcel, der Modul-Builder, den ich kürzlich studiert habe, hat ebenso wie React einen Paketordner. Das Schlüsselmodul heißt Paketbündler und enthält Code, der für die Erstellung von Assemblys, den Betrieb des Modulaktualisierungsservers (Hot Module Server) und des Befehlszeilentools verantwortlich ist.
Wenn Sie den Quellcode bald analysieren, lesen Sie die JavaScript-Spezifikationen.Ein weiteres Plus, das mich sehr überrascht hat, ist, dass Sie die offizielle JavaScript-Spezifikation leichter lesen können. Das erste Mal habe ich mich an sie gewandt, als ich versucht habe herauszufinden, was der Unterschied zwischen Wurffehler und Wurffehler ist (Spoiler -
nichts ). Ich habe diese Frage gestellt, weil Mithril bei der Implementierung der m-Funktion einen Wurffehler verwendet hat und ich mich gefragt habe, warum es besser ist, als einen neuen Fehler zu werfen. Dann habe ich auch erfahren, dass die Operatoren && und || Ich habe die
Regeln gefunden, nach denen der Operator des nicht strengen Vergleichs == die Werte "auflöst" und den
Grund, warum Object.prototype.toString.call ({}) '[object Object]' zurückgibt.
So analysieren Sie den Quellcode
Es gibt viele Möglichkeiten, den Quellcode zu analysieren. Der einfachste Weg scheint mir der folgende zu sein: Wählen Sie eine Methode aus Ihrer Bibliothek aus und beschreiben Sie, was passiert, wenn Sie sie aufrufen. Es lohnt sich nicht, jeden Schritt zu beschreiben, Sie müssen nur versuchen, seine allgemeinen Prinzipien und Strukturen zu verstehen.
Kürzlich habe ich ReactDOM.render auf diese Weise analysiert und viel über React Fibre und einige der Schwierigkeiten bei der Implementierung gelernt. Glücklicherweise ist React sehr beliebt und das Vorhandensein einer großen Anzahl von Artikeln zum gleichen Thema von anderen Entwicklern hat den Prozess beschleunigt.
Dieser Einblick in den Code führte mich auch in das Konzept der
kooperativen Planung , die
window.requestIdleCallback- Methode und ein Live-
Beispiel einer verknüpften Liste ein (Reagiert verarbeitet Aktualisierungen, indem sie an die Warteschlange
gesendet werden, bei der es sich um eine vorrangig verknüpfte Liste von Aktualisierungen handelt). Dabei wäre es schön, mit der Bibliothek eine einfache Anwendung zu erstellen. Dies erleichtert das Debuggen, da Sie sich nicht mit dem Stack-Trace anderer Bibliotheken befassen müssen.
Wenn ich keine detaillierte Überprüfung durchführe, öffne ich den Ordner node_modules in dem Projekt, an dem ich arbeite, oder schaue mir GitHub an. Ich mache das immer, wenn ich auf einen Fehler oder eine interessante Funktion stoße. Stellen Sie beim Lesen des Codes auf GitHub sicher, dass dies die neueste Version ist. Der Code der neuesten Version wird angezeigt, indem Sie auf die Schaltfläche zum Ändern der Zweige klicken und "Tags" auswählen. Änderungen an Bibliotheken und Frameworks werden derzeit durchgeführt, sodass Sie wahrscheinlich nicht etwas analysieren möchten, das möglicherweise nicht in der nächsten Version enthalten ist.
Eine oberflächlichere Version des Lernens von Quellcode ist das, was ich als "Schnellblick" bezeichne. Irgendwie habe ich express.js installiert, den Ordner node_modules geöffnet und die Abhängigkeiten durchgesehen. Wenn README mir keine zufriedenstellende Erklärung gegeben hat, habe ich die Quelle gelesen. Dies führte mich zu interessanten Entdeckungen:
- Express verwendet zwei Module zum Zusammenführen von Objekten, und die Funktionsweise dieser Module ist sehr unterschiedlich. Merge-Deskriptoren fügen nur die im Quellobjekt gefundenen Eigenschaften hinzu und fügen auch nicht aufzählbare Eigenschaften hinzu, während utils-merge die aufgezählten Eigenschaften des Objekts und seiner gesamten Prototypkette behandelt. Merge-Descriptors verwendet Object.getOwnPropertyNames () und Object.getOwnPropertyDescriptor (), und Utils-Merge verwendet for..in;
- Das Modul setprototypeof bietet eine plattformübergreifende Option zum Angeben des Prototyps des erstellten (instanziierten) Objekts.
- Escape-HTML ist ein Escape-Modul mit 78 Zeilen, nach dem der Inhalt in HTML eingefügt werden kann.
Obwohl diese Entdeckungen höchstwahrscheinlich nicht sofort nützlich sind, ist ein allgemeines Verständnis der Abhängigkeiten Ihrer Bibliothek oder Ihres Frameworks sehr hilfreich.
Debugging-Browser-Tools sind Ihre besten Freunde beim Debuggen von Code im Frontend. Unter anderem können Sie das Programm jederzeit stoppen und gleichzeitig seinen Status überprüfen, die Funktion überspringen oder sie betreten oder verlassen. In minimiertem Code ist dies nicht möglich - deshalb entpacke ich diesen Code und lege ihn in die entsprechende Datei im Ordner node_modules.
Verwenden Sie den Debugger als nützliche Anwendung. Machen Sie eine Annahme und testen Sie sie dann.Fallstudie: Verbindungsfunktion in Redux
React-Redux ist eine Bibliothek zum Verwalten des Status von React-Anwendungen. Wenn ich mit solchen populären Bibliotheken arbeite, suche ich zunächst nach Artikeln über deren Verwendung. Bei der Vorbereitung dieses Beispiels habe ich diesen
Artikel überprüft. Dies ist ein weiterer Pluspunkt beim Erlernen des Quellcodes. Er führt Sie zu informativen Artikeln wie diesem, die Ihr Denken und Verstehen verbessern.
Connect ist eine React-Redux-Funktion, die die React-Komponente und den Redux-Speicher einer Anwendung verbindet. Wie? Laut
Dokumentation macht sie Folgendes:
"... gibt eine neue verwandte Komponentenklasse zurück, die ein Wrapper der an sie übergebenen Komponente ist."
Nachdem ich dies gelesen habe, stelle ich folgende Fragen:
- Kenne ich Muster oder Konzepte, bei denen Funktionen Eingabeparameter mit zusätzlichen Funktionen zurückgeben?
- Wenn ja, wie verwende ich dies basierend auf der Beschreibung aus der Dokumentation?
Normalerweise besteht der nächste Schritt darin, eine primitive Anwendung mit der Verbindungsfunktion zu erstellen. Trotzdem habe ich in dieser Situation eine neue Anwendung für React verwendet, an der wir gearbeitet haben, weil ich Connect im Kontext einer Anwendung verstehen wollte, die höchstwahrscheinlich bald in Produktion gehen würde.
Die Komponente, auf die ich mich konzentriert habe, sieht ungefähr so aus:
class MarketContainer extends Component {
Dies ist eine Containerkomponente, die als Wrapper für die vier kleineren verwandten Komponenten dient. Eines der ersten Dinge in der
Datei , die Connect exportiert, ist der Kommentar „Connect ist die Fassade für connectAdvanced“. Bereits in dieser Phase können wir etwas lernen: Wir haben die Möglichkeit, das Fassadenmuster in Aktion zu beobachten. Am Ende der Datei sehen wir, wie connect einen Aufruf an die Funktion createConnect exportiert. Seine Parameter sind eine Reihe von Standardwerten, die wie folgt destrukturiert sind:
export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {})
Und wir haben noch einen lehrreichen Moment: Export der aufgerufenen Funktion und standardmäßige Destrukturierung der Funktionsargumente. Die Umstrukturierung ist für uns aufschlussreich, da der Code folgendermaßen geschrieben werden könnte:
export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory })
Als Ergebnis würden wir einen Fehler erhalten - Uncaught TypeError: Die Eigenschaft 'connectHOC' von 'undefined' oder 'null' kann nicht zerstört werden. Dies würde passieren, weil die Funktion keine Standardargumentwerte hat.
Hinweis: Um die Umstrukturierung von Argumenten besser zu verstehen, können Sie den Artikel von David Walsh lesen. Abhängig von Ihren Sprachkenntnissen mögen einige Punkte trivial erscheinen. Dann können Sie sich auf die Punkte konzentrieren, mit denen Sie nicht vertraut sind.Die Funktion createConnect selbst führt nichts aus. Es gibt nur die Verbindungsfunktion zurück, die ich hier verwendet habe:
export default connect(null, mapDispatchToProps)(MarketContainer)
Es werden vier optionale Argumente benötigt, und die ersten drei durchlaufen die
Übereinstimmungsfunktion , mit deren Hilfe sie ihr Verhalten anhand der übergebenen Argumente sowie ihres Typs bestimmen können. Es stellt sich heraus, dass ich wählen muss, wohin ich als nächstes gehen soll, da das zweite Argument, das an match übergeben wird, eine der drei in connect importierten Funktionen ist.
Es gibt auch etwas zu lernen von der
Proxy-Funktion, die zum Umschließen des ersten Arguments in connect verwendet wird, wenn diese Argumente Funktionen sind. über das Dienstprogramm
isPlainObject , mit dem einfache Objekte überprüft werden, oder über das
Warnmodul , das zeigt, wie Sie einen Debugger
erstellen können, der bei allen Fehlern funktioniert. Nach der Match-Funktion fahren wir mit connectHOC fort, der Funktion, die unsere Reaktionskomponente aufnimmt und sie mit Redux verknüpft. Es gibt einen weiteren Funktionsaufruf, der
wrapWithConnect zurückgibt - eine Funktion, die die Bindung der Komponente an das Repository übernimmt.
Wenn ich mir die connectHOC-Implementierung anschaue, kann ich mir vorstellen, warum die Details der connect-Implementierung ausgeblendet werden sollten. Dies ist im Wesentlichen das Herzstück von React-Redux und enthält Logik, auf die über Connect nicht zugegriffen werden sollte. Selbst wenn wir uns damit befassen, haben wir später, wenn wir tiefer graben müssen, bereits das Quellmaterial mit einer detaillierten Erläuterung des Codes.
Fassen Sie zusammen
Das Erlernen des Quellcodes ist zunächst sehr kompliziert. Aber wie alles andere wird es mit der Zeit einfacher. Seine Aufgabe ist es nicht, alles zu verstehen, sondern etwas Nützliches für sich herauszubringen - ein gemeinsames Verständnis und neues Wissen. Es ist sehr wichtig, während des gesamten Prozesses vorsichtig zu sein und sich mit den Details zu befassen.
Zum Beispiel fand ich die Funktion isPlainObject interessant, weil sie diese verwendet, wenn (typeof obj! == 'object' || obj === null) false zurückgibt, um sicherzustellen, dass das übergebene Argument ein einfaches Objekt ist. Als ich diesen Code zum ersten Mal las, dachte ich mir, warum nicht einfach Object.prototype.toString.call (opts)! == '[object Object]' verwenden, um den Code zu reduzieren und Objekte von ihren Untertypen wie Date zu trennen. Aber bereits in der nächsten Zeile ist klar, dass selbst wenn plötzlich (plötzlich!) Ein Entwickler, der connect verwendet, ein Date-Objekt zurückgibt, z. B. durch Überprüfen von Object.getPrototypeOf (obj) === null.
Ein weiterer unerwarteter Punkt in isPlainObject an dieser Stelle:
while (Object.getPrototypeOf(baseProto) !== null) { baseProto = Object.getPrototypeOf(baseProto) }
Das Finden einer Antwort bei Google führte mich zu
diesem Thread in StackOverflow und zu
diesem Kommentar zu GitHubs Redux, in dem erläutert wird, wie dieser Code Situationen behandelt, in denen beispielsweise ein Objekt von einem iFrame übertragen wird.
- -
Zuerst beschlossen, den Artikel zu übersetzen. Ich wäre dankbar für Klarstellungen, Ratschläge und Empfehlungen