Wie Chrome DevTools vom Fahrrad zum Standard gewechselt ist





Ein kurzer Hinweis zur Migration der Chrome DevTools vom internen Modullader auf Standard-JavaScript-Module. Wir werden Ihnen erklären, wie und warum sich die Migration verzögert hat, welche versteckten Kosten für die Migration anfallen und welche Schlussfolgerungen das DevTools-Team nach Abschluss der Migration gezogen hat. Beginnen wir jedoch mit der Geschichte der Webentwickler-Tools.



Einführung



Wie Sie wahrscheinlich wissen, ist Chrome DevTools eine HTML-, CSS- und JavaScript-Webanwendung. Im Laufe der Jahre wurde DevTools funktionsreich, intelligent und kennt sich mit der modernen Webplattform aus. Obwohl DevTools erweitert wurde, erinnert seine Architektur weitgehend an das Original, als das Tool Teil von WebKit war .



Wir werden die Geschichte von DevTools erzählen, die Vor- und Nachteile der Lösungen beschreiben und was wir getan haben, um diese Einschränkungen abzumildern. Kommen wir also zu modularen Systemen, zum Laden von Code und zur Verwendung von JavaScript-Modulen.



Am Anfang war nichts



Das Frontend verfügt nun über viele modulare Systeme und deren Tools sowie ein standardisiertes JavaScript-Modulformat . Nichts davon war, als DevTools gestartet wurde. Das Tool basiert auf WebKit-Code, der vor über 12 Jahren geschrieben wurde.



Die erste Erwähnung des modularen Systems in DevTools stammt aus dem Jahr 2012: Es war die Einführung einer Liste von Modulen mit einer entsprechenden Liste von Quellen . Ein Teil der Python-Infrastruktur, die zu dieser Zeit zum Kompilieren und Erstellen von DevTools verwendet wurde. Im Jahr 2013 wurden die Module durch frontend_modules.json dieses Commit in eine Datei und 2014 in separate Dateien module.json( hier ) ausgecheckt . Beispiel module.json:



{
  "dependencies": [
    "common"
  ],
  "scripts": [
    "StylePane.js",
    "ElementsPanel.js"
  ]
}


Seit 2014 module.jsonin Entwicklertools verwendet, um auf Module und Quelldateien zu verweisen. In der Zwischenzeit wuchs das Web-Ökosystem schnell und es wurden viele Modulformate erstellt: UMD, CommonJS und schließlich standardisierte JavaScript-Module. DevTools steckt jedoch fest module.json. Das nicht standardisierte, einzigartige modulare System hatte mehrere Nachteile:



  1. module.json benötigte seine eigenen Build-Tools.
  2. Es gab keine IDE-Integration. Natürlich benötigte sie spezielle Tools, um Dateien zu erstellen, die sie verstand: ( jsconfig.jsonfür VS Code ).
  3. Funktionen, Klassen und Objekte wurden in den globalen Bereich eingefügt, um die gemeinsame Nutzung zwischen Modulen zu ermöglichen.
  4. Die Reihenfolge, in der die Dateien aufgelistet wurden, war wichtig. Es gab keine Garantie dafür, dass der Code, auf den Sie sich verlassen, außer der Überprüfung durch den Menschen hochgeladen wurde.


Bei der Bewertung des aktuellen Status des modularen DevTools-Systems und anderer weit verbreiteter Modulformate kamen wir im Allgemeinen zu dem Schluss, dass es module.jsonmehr Probleme verursachte als löste.



Vorteile des Standards



Wir haben JavaScript-Module ausgewählt. Als diese Entscheidung getroffen wurde, waren Module in der Sprache noch in Node.js markiert, und eine große Anzahl von NPM-Paketen unterstützte sie nicht. Unabhängig davon kamen wir zu dem Schluss, dass JavaScript-Module die beste Option sind.



Der Hauptvorteil von Modulen besteht darin, dass sie ein sprachstandardisiertes Format sind . Als wir die Nachteile aufgelistet habenmodule.jsonWir stellten fest, dass fast alle mit der Verwendung eines nicht standardisierten, einzigartigen Modulformats zusammenhängen. Die Wahl eines nicht standardisierten Modulformats bedeutet, dass wir Zeit für die Erstellung von Integrationen mit Build-Tools und den Tools unserer Kollegen investieren müssen. Diese Integrationen waren oft fragil und hatten keine Unterstützung für Funktionen, was zusätzliche Wartungszeit und manchmal knifflige Fehler erforderte. Die Fehler trafen die Benutzer.



Da JavaScript-Module Standard waren, bedeutete dies, dass IDEs wie VS Code, Typprüfungstools wie der Closure / TypeScript-Compiler und Build-Tools wie Rollup und Minifiers den geschriebenen Quellcode verstehen konnten. Wenn eine neue Person dem DevTools-Team beitritt, muss sie keine Zeit damit verschwenden, das proprietäre Team zu erlernen module.json.



Als DevTools zum ersten Mal gestartet wurde, gab es natürlich keinen der oben genannten Vorteile. Die Implementierung der Laufzeit dauerte Jahre in Standardgruppen. Es dauerte einige Zeit, bis die Entwickler - Benutzer der Module - Feedback gaben. Als Module in der Sprache erschienen, hatten wir die Wahl: entweder unser eigenes Format weiter zu unterstützen oder in den Übergang zu einem neuen Format zu investieren.



Wie viel kostet der Glanz der Neuheit?



Obwohl JavaScript-Module viele Vorteile hatten, die wir nutzen wollten, blieben wir in der Welt module.json. Die Nutzung der Sprachmodule bedeutete, dass wir erhebliche Anstrengungen in technische Schulden investieren mussten. In der Zwischenzeit könnte die Migration Funktionen unterbrechen und Regressionsfehler verursachen.



Es ging nicht darum, ob wir JavaScript-Module verwenden sollten. Die Frage war, wie teuer die Verwendung von JavaScript-Modulen ist . Wir mussten das Risiko, Benutzer zu ärgern, mit Regressionen, der Zeit, die Ingenieure für die Migration benötigten, und einer Zeit der Verschlechterung des Systemzustands, in dem wir arbeiten würden, in Einklang bringen.



Der letzte Punkt erwies sich als sehr wichtig. Obwohl wir theoretisch zu JavaScript-Modulen gelangen könnten, würden wir während der Migration Code erhalten, der beide Modultypen berücksichtigt . Dies ist nicht nur eine technische Herausforderung, sondern bedeutet auch, dass alle an DevTools arbeitenden Ingenieure wissen müssen, wie man in einer solchen Umgebung arbeitet. Sie müssten sich ständig fragen: "Was ist in diesem Code los, ist es module.jsonJS, und wie kann ich die Änderung vornehmen?"

Die latenten Migrationskosten für Schulungskollegen waren höher als erwartet.
Nach Analyse der Kosten kamen wir zu dem Schluss, dass es sich immer noch lohnt, auf Module in der Sprache umzusteigen. Daher waren unsere Hauptziele:



  1. Stellen Sie sicher, dass die Standardmodule so nützlich wie möglich sind.
  2. Stellen Sie sicher, dass die Integration in vorhandene Module auf der Basis module.jsonsicher ist und keine negativen Auswirkungen auf den Benutzer hat (Regressionsfehler, Benutzerfrustration).
  3. Stellen Sie DevTools-Migrationshandbücher bereit. In erster Linie durch in den Prozess integrierte Checks and Balances, um versehentliche Fehler zu vermeiden.


Tabellenkalkulation, Conversions und technische Schulden



Das Ziel war klar. Aber die Einschränkungen waren module.jsonschwer zu umgehen. Es dauerte mehrere Iterationen, Prototypen und architektonische Änderungen, bis wir eine brauchbare Lösung fanden. Am Ende haben wir ein Projektdokument mit einer Migrationsstrategie geschrieben. Dieses Dokument gab eine erste Schätzung der Zeit: 2-4 Wochen.

Der intensivste Teil der Migration dauerte 4 Monate und 7 Monate vergingen von Anfang bis Ende!
Der ursprüngliche Plan hat sich jedoch bewährt: Wir wollten der DevTools-Laufzeit beibringen, alle Dateien auf die alte Weise zu laden, um die im Array scripts module.jsonaufgelisteten Dateien zu verwenden , während alle im Array aufgelisteten Dateien modulesdurch dynamischen Sprachimport geladen werden mussten . Jede Datei, die sich im Array befindet, moduleskann mit importund exportvon ES6 arbeiten.



Außerdem wollten wir in 2 Phasen migrieren. Letztendlich haben wir die letzte Phase in zwei Unterphasen aufgeteilt: Export und Import. Module und Phasen wurden in einer großen Tabelle verfolgt:





Ein Ausschnitt aus der Migrationstabelle hier.



Exportphase



Der erste Schritt bestand darin, Exportanweisungen für alle Entitäten hinzuzufügen, die von Modulen / Dateien gemeinsam genutzt werden müssen. Die Umwandlung wurde automatisiert, indem für jeden Ordner ein Skript ausgeführt wurde . Nehmen wir an, module.jsones gibt eine solche Einheit:



Module.File1.exported = function() {
  console.log('exported');
  Module.File1.localFunctionInFile();
};
Module.File1.localFunctionInFile = function() {
  console.log('Local');
};


Hier Moduleist der Name des Moduls. File1- Dateiname. Im Codebaum sieht es so aus : front_end/module/file1.JS.



Der obige Code übersetzt dies:



export function exported() {
  console.log('exported');
  Module.File1.localFunctionInFile();
}
export function localFunctionInFile() {
  console.log('Local');
}

/** Legacy export object */
Module.File1 = {
  exported,
  localFunctionInFile,
};


Wir hatten ursprünglich geplant, den Import zu diesem Zeitpunkt in eine einzelne Datei umzuschreiben. Zum Beispiel in dem obigen Beispiel würden schreiben wir Module.File1.localFunctionInFilean localFunctionInFile. Wir haben jedoch festgestellt, dass es einfacher und sicherer ist, die beiden Transformationen zu trennen. Somit wird "Übertragen aller Entitäten in eine Datei" zur zweiten Import-Unterphase.



Da durch Hinzufügen eines Schlüsselworts exportdie Datei von einem "Skript" in ein "Modul" umgewandelt wird, musste ein Großteil der DevTools-Infrastruktur entsprechend aktualisiert werden. Das Framework enthielt eine dynamische Importlaufzeit sowie Tools wie ESLint für die Ausführung im Modulmodus.



Ein Ärgernis war, dass unsere Tests in einem "nicht strengen" Modus durchgeführt wurden. JavaScript-Module implizieren, dass die Dateien im strengen Modus ausgeführt werden. Dies wirkte sich auf die Tests aus. Wie sich herausstellte, beruhte eine nicht triviale Anzahl von Tests auf einem nicht strengen Modus, einschließlich eines Tests, bei dem der Bediener anwesend war with.



Am Ende dauerte das Aktualisieren des allerersten Ordners (Hinzufügen von Export) ungefähr eine Woche und mehrere Versuche, ihn neu zu laden .



Importphase



Nachdem alle Entitäten mithilfe der Exportanweisungen exportiert wurden und aufgrund von Legacy im globalen Bereich blieben, mussten wir alle Entitätsreferenzen aktualisieren, wenn sie sich in mehreren Dateien befinden, um ES-Importe zu verwenden. Das ultimative Ziel ist es, alle abgelaufenen Exporte zu entfernen, indem der globale Geltungsbereich gelöscht wird. Die Umwandlung wurde automatisiert, indem für jeden Ordner ein Skript ausgeführt wurde .



Zum Beispiel die folgenden Entitäten module.json:



Module.File1.exported();
AnotherModule.AnotherFile.alsoExported();
SameModule.AnotherFile.moduleScoped();


Konvertiert zu:



import * as Module from '../module/Module.js';
import * as AnotherModule from '../another_module/AnotherModule.js';

import {moduleScoped} from './AnotherFile.js';

Module.File1.exported();
AnotherModule.AnotherFile.alsoExported();
moduleScoped();


Dieser Ansatz weist jedoch folgende Einschränkungen auf:



  1. Nicht jede Entität wurde grundsätzlich benannt Module.File.symbolName. Einige Entitäten wurden benannt Modele.Fileoder sogar Module.CompletelyDifferentName. Die Nichtübereinstimmung bedeutete, dass wir eine interne Zuordnung vom alten globalen Objekt zum neuen importierten Objekt erstellen mussten.
  2. moduleScoped. , Events, , Events. , , , import Events .
  3. , . , . , , . , , ( DevTools). , , .


JavaScript



Im Februar 2020, 6 Monate nach dem Start im September 2019, wurden die letzten Bereinigungen im Ordner ui / durchgeführt. So endete die Migration inoffiziell. Als sich der Staub gelegt hatte, haben wir die Migration am 5. März 2020 offiziell als abgeschlossen markiert .



Jetzt funktionieren DevTools nur noch mit JavaScript-Modulen. Wir stellen noch einige Entitäten in den globalen Bereich (in Legacy-Dateien module.js) für Legacy-Tests oder Integrationen mit anderen Teilen der Architektenwerkzeuge. Sie werden im Laufe der Zeit entfernt, aber wir betrachten sie nicht als Blockierung der Entwicklung. Wir haben auch einen Styleguide für JavaScript-Module .



Statistiken



Konservative Schätzungen der Anzahl von CL (Änderungsliste - ein Begriff, der in Gerrit verwendet wird, ähnlich der GitHub-Pull-Anforderung), die an dieser Migration beteiligt sind, liegen bei etwa 250 CL, meistens von 2 Ingenieuren . Wir haben keine endgültigen Statistiken über die Größe der vorgenommenen Änderungen, aber eine konservative Schätzung der geänderten Zeilen (die Summe der absoluten Differenz zwischen Einfügungen und Löschungen für jeden CL) beträgt ungefähr 30.000 Zeilen, was ungefähr 20% des gesamten DevTools-Front-End-Codes entspricht .



Die erste exportierte Datei wird in Chrome 79 unterstützt, das im Dezember 2019 in der stabilen Version veröffentlicht wurde. Die letzte Änderung für den Wechsel zum Import erfolgt in Chrome 83, das im Mai 2020 in der stabilen Version veröffentlicht wurde.



Uns ist eine Regression aufgrund der Migration in stabilem Chrome bekannt. Auto - Vervollständigung von Code - Schnipsel in der Befehlsleiste brach wegen Fremdstandardexport . Es gab mehrere andere Regressionen, aber unsere automatisierten Testfälle und Chrome Canary-Benutzer haben sie gemeldet. Wir haben Fehler behoben, bevor sie in stabile Chrome-Versionen gelangen konnten.



Sie können die ganze Geschichte sehen, indem Sie sich hier registrieren . Nicht alle, aber die meisten CL sind an diesen Fehler gebunden.



Was haben wir gelernt?



  1. . , JavaScript ( ) , DevTools . , , , .
  2. — , . , , . , , , .
  3. (, ) . -. , Python Rollup.
  4. (~20% ), . , , . , .
  5. , . . , , , . , , — . , , .


Bild


, Level Up , - SkillFactory:





E







All Articles