VorwortFront-End-Entwickler, die sich mit dem vue.js-Framework vertraut gemacht haben, kennen sich möglicherweise mit Single-File-Komponenten aus. Einzeldateikomponenten in Vue.js ermöglichen, den gesamten Inhalt einer Komponente in einer einzigen Datei zu definieren. Dies ist eine sehr nützliche Lösung und wurde bereits für Browser-Webseiten empfohlen. Doch leider hat es seit der Vorstellung dieses Konzepts im August 2017 bisher keine Fortschritte gegeben und es scheint, als würde es aussterben. Dennoch ist es interessant und lohnenswert, tiefer in die Thematik einzusteigen und die Umsetzung von Single-File-Komponenten mithilfe vorhandener Technologien zu versuchen. Einzelne DateikomponentenFrontend-Entwickler, die das Konzept der „progressiven Verbesserung“ kennen, haben sicherlich auch schon vom Konzept der „Schichtung“ gehört. Auch bei Komponenten gibt es ein solches Konzept. Tatsächlich hat jede Komponente mindestens drei oder sogar mehr Ebenen: Inhalt/Vorlage, Präsentation und Verhalten. Oder, um konservativ zu sein, wird jede Komponente in mindestens 3 Dateien aufgeteilt. Die Dateistruktur einer Schaltflächenkomponente kann beispielsweise wie folgt aussehen:
Eine solche Schichtung entspricht einer Trennung der Technologien (Inhalt/Vorlage: HTML verwenden, Präsentation: CSS verwenden, Verhalten: JavaScript verwenden). Wenn zum Bündeln kein Build-Tool verwendet wird, bedeutet dies, dass der Browser diese 3 Dateien abrufen muss. Daher besteht eine Idee darin, dass zur Lösung dieses Problems dringend eine Technologie zur Trennung von Komponentencodes ohne Trennung von Technologien (Dateien) erforderlich ist. Darum geht es in diesem Artikel: Einzeldateikomponenten. Ich stehe dem „Technology Layering“ grundsätzlich skeptisch gegenüber. Dies ist auf die Tatsache zurückzuführen, dass häufig auf die Komponentenschichtung verzichtet wird, um die völlig separate „Technologieschichtung“ zu umgehen. Zurück zum Thema. Die Implementierung einer Schaltfläche mit einer Einzeldateikomponente könnte folgendermaßen aussehen: <Vorlage> <!-- Hierher kommt der Inhalt von Button.html. --> </Vorlage> <Stil> /* Hierher kommt der Inhalt von Button.css. */ </Stil> <Skript> // Hierher kommt der Inhalt von Button.js. </Skript> Sie können sehen, dass diese Einzeldateikomponente dem HTML-Dokument in der anfänglichen Front-End-Entwicklung sehr ähnlich ist. Sie verfügt über ein eigenes Stil-Tag und ein eigenes Skript-Tag, aber die Präsentationsebene verwendet ein Vorlagen-Tag. Dank des einfachen Ansatzes erhalten Sie eine leistungsstarke mehrschichtige Komponente (Inhalt/Vorlage: <Vorlage>, Präsentation: <Stil>, Verhalten: <Skript>), ohne drei separate Dateien verwenden zu müssen. Grundlegende KonzepteZuerst erstellen wir eine globale Funktion loadComponent(), um die Komponente zu laden. fenster.loadComponent = (Funktion() { Funktion loadComponent( URL ) {} loadComponent zurückgeben; }()); Hier kommt das JavaScript-Modulmuster zum Einsatz. Es ermöglicht die Definition aller notwendigen Hilfsfunktionen, stellt aber nur die Funktion loadComponent() extern bereit. Natürlich ist diese Funktion im Moment leer. Später erstellen wir eine <hallo-welt>-Komponente, um den folgenden Inhalt anzuzeigen.
Klicken Sie außerdem auf diese Komponente und es erscheint die folgende Meldung:
Der Komponentencode wird als Datei HelloWorld.wc gespeichert (hier steht .wc für Web Component). Der ursprüngliche Code lautet wie folgt: <Vorlage> <div Klasse="hallo"> <p>Hallo Welt! Mein Name ist <slot></slot>.</p> </div> </Vorlage> <Stil> div { Hintergrund: rot; Rahmenradius: 30px; Polsterung: 20px; Schriftgröße: 20px; Textausrichtung: zentriert; Breite: 300px; Rand: 0 automatisch; } </Stil> <Skript></Skript> Derzeit wurde der Komponente kein Verhalten hinzugefügt, nur die Vorlage und die Stile wurden definiert. In der Vorlage können Sie allgemeine HTML-Tags wie <div> verwenden. Darüber hinaus wird in der Vorlage das Element <slot> angezeigt, um anzuzeigen, dass die Komponente Shadow DOM implementiert. Und standardmäßig sind alle Stile und Vorlagen dieses DOM selbst in diesem DOM ausgeblendet. Die Art und Weise, wie Komponenten in Webseiten verwendet werden, ist sehr einfach. <hallo-welt>Kommandeur</hallo-welt> <script src="loader.js"></script> <Skript> loadComponent('HelloWorld.wc'); </Skript> Komponenten können wie standardmäßige benutzerdefinierte Elemente verwendet werden. Der einzige Unterschied besteht darin, dass Sie es laden müssen, bevor Sie die Methode loadComponent() verwenden (diese Methode befindet sich in loader.js). Die Methode loadComponent() übernimmt die ganze schwere Arbeit, wie das Abrufen der Komponente und ihre Registrierung bei customElements.define(). Nachdem Sie nun alle Konzepte verstanden haben, ist es an der Zeit, praktisch anzufangen. Einfacher LaderWenn Sie Dateien aus externen Dateien laden möchten, müssen Sie das universelle Ajax verwenden. Aber jetzt schreiben wir das Jahr 2020 und in den meisten Browsern können Sie die Fetch-API sicher verwenden. Funktion loadComponent( URL ) { gib fetch( URL ) zurück; } Dadurch wird die Datei jedoch nur abgerufen, ohne dass eine Verarbeitung erfolgt. Als Nächstes müssen Sie den Ajax-Rückgabeinhalt wie folgt in Text umwandeln: Funktion loadComponent( URL ) { returniere fetch( URL ).then( ( Antwort ) => { returniere Antworttext(); } ); } Da die Funktion loadComponent() das Ausführungsergebnis der Fetch-Funktion zurückgibt, handelt es sich um ein Promise-Objekt. Ob die Datei (HelloWorld.wc) tatsächlich geladen wird und ob sie in Text umgewandelt wird, können Sie in der then-Methode prüfen: Die Ergebnisse sind wie folgt: Im Chrome-Browser können wir mithilfe der Methode console() sehen, dass der Inhalt von HelloWorld.wc in Text umgewandelt und ausgegeben wird. Es scheint also zu funktionieren! Komponenteninhalt analysierenMit der bloßen Ausgabe des Textes erreichen wir unser Ziel allerdings nicht. Letztendlich muss es zur Anzeige in DOM konvertiert werden und kann tatsächlich mit dem Benutzer interagieren. Im Browserumfeld gibt es eine sehr praktische Klasse DOMParser, mit der sich ein DOM-Parser erstellen lässt. Instanziieren Sie eine DOMParser-Klasse, um ein Objekt zu erhalten, das zum Konvertieren von Komponententext in DOM verwendet werden kann: fenster.loadComponent = (Funktion () { Funktion loadComponent(URL) { returniere fetch(URL).then((Antwort) => { returniere Antworttext(); }).then((html) => { const parser = neuer DOMParser(); // 1 return parser.parseFromString(html, 'text/html'); // 2 }); } loadComponent zurückgeben; }()); Zuerst wird ein DOMParser-Instanz-Parser erstellt (1), und dann wird diese Instanz verwendet, um den Komponenteninhalt in DOM zu konvertieren (2). Zu beachten ist, dass hier der HTML-Modus (,text/html‘) verwendet wird. Wenn Ihr Code besser dem JSX-Standard oder der ursprünglichen Vue.js-Komponente entsprechen soll, können Sie den XML-Modus („text/XML“) verwenden. In diesem Fall muss jedoch die Struktur der Komponente selbst geändert werden (z. B. durch Hinzufügen eines Hauptelements, das andere Elemente enthalten kann). Dies ist das Rückgabeergebnis der Funktion loadComponent(), ein DOM-Baum. Im Chrome-Browser gibt console.log() die analysierte Datei HelloWorld.wc aus, bei der es sich um einen DOM-Baum handelt. Beachten Sie, dass die Methode parser.parseFromString der Komponente automatisch die Tag-Elemente <html>, <head> und <body> hinzufügt. Dies liegt an der Funktionsweise des HTML-Parsers. Der Algorithmus zum Erstellen des DOM-Baums wird in der HTML-LS-Spezifikation ausführlich beschrieben. Dies ist ein langer Artikel und das Lesen wird einige Zeit in Anspruch nehmen. Einfach ausgedrückt lässt sich aber sagen, dass der Parser standardmäßig den gesamten Inhalt in das <head>-Element einfügt, bis er auf ein DOM-Element stößt, das nur innerhalb eines <body>-Tags platziert werden kann. Daher dürfen alle Elemente im Komponentencode (<element>, <style>, <script>) in <head> platziert werden. Wenn Sie ein <p>-Element in ein <template> einschließen, fügt der Parser es in den <body> ein. Es gibt noch ein weiteres Problem. Nachdem die Komponente analysiert wurde, gibt es keine <!DOCTYPE html>-Deklaration. Dies ist also ein abnormales HTML-Dokument. Der Browser verwendet daher eine Methode namens Quirks-Modus, um dieses HTML-Dokument darzustellen. Glücklicherweise hat es hier keine negativen Auswirkungen, da der DOM-Parser hier nur dazu verwendet wird, die Komponente in entsprechende Teile aufzuteilen. Nachdem wir den DOM-Baum haben, können wir nur den Teil extrahieren, den wir benötigen. returniere fetch(URL).then((Antwort) => { returniere Antworttext(); }).then((html) => { const parser = neuer DOMParser(); const document = parser.parseFromString(html, 'text/html'); const head = Dokument.Kopf; const Vorlage = head.querySelector('Vorlage'); const Stil = head.querySelector('Stil'); const Skript = head.querySelector('Skript'); zurückkehren { Vorlage, Stil, Skript }; }); Lassen Sie uns abschließend den Code organisieren. Die Methode loadComponent lautet wie folgt. fenster.loadComponent = (Funktion () { Funktion fetchAndParse(URL) { returniere fetch(URL).then((Antwort) => { returniere Antworttext(); }).then((html) => { const parser = neuer DOMParser(); const document = parser.parseFromString(html, 'text/html'); const head = Dokument.Kopf; const Vorlage = head.querySelector('Vorlage'); const Stil = head.querySelector('Stil'); const Skript = head.querySelector('Skript'); zurückkehren { Vorlage, Stil, Skript }; }); } Funktion loadComponent(URL) { gibt fetchAndParse(URL) zurück; } loadComponent zurückgeben; }()); Die Fetch-API ist nicht die einzige Möglichkeit, Komponentencode aus externen Dateien abzurufen. XMLHttpRequest verfügt über einen dedizierten Dokumentmodus, mit dem Sie den gesamten Analyseschritt überspringen können. Aber XMLHttpRequest gibt kein Promise zurück, daher müssen Sie es selbst verpacken. Registrieren von KomponentenDa wir nun über eine Komponentenebene verfügen, können wir eine Methode „registerComponent()“ erstellen, um neue benutzerdefinierte Komponenten zu registrieren. fenster.loadComponent = (Funktion () { Funktion fetchAndParse(URL) { […] } Funktion registerComponent() { } Funktion loadComponent(URL) { gibt fetchAndParse(URL).then(registerComponent) zurück; } loadComponent zurückgeben; }()); Es ist zu beachten, dass die benutzerdefinierte Komponente eine Klasse sein muss, die von HTMLElement erbt. Darüber hinaus verwendet jede Komponente ein Shadow-DOM zum Speichern von Stilen und Vorlageninhalten. Daher hat diese Komponente bei jeder Referenzierung den gleichen Stil. So geht's: Funktion registerComponent({Vorlage, Stil, Skript}) { Klasse UnityComponent erweitert HTMLElement { verbundenerCallback() { dies._upcast(); } _upcast() { const shadow = this.attachShadow({mode: 'open'}); shadow.appendChild(style.cloneNode(true)); shadow.appendChild(document.importNode(template.content, true)); } } } Die Klasse UnityComponent sollte innerhalb der Methode registerComponent() erstellt werden, da diese Klasse die an registerComponent() übergebenen Parameter verwendet. Diese Klasse implementiert Shadow DOM mithilfe eines leicht modifizierten Mechanismus, den ich in diesem Artikel über Shadow DOM (auf Polnisch) ausführlich beschreibe. Jetzt fehlt nur noch eines, um die Komponente zu registrieren: Geben Sie der Einzeldateikomponente einen Namen und fügen Sie sie dem DOM der aktuellen Seite hinzu. Funktion registerComponent({ Vorlage, Stil, Skript }) { Klasse UnityComponent erweitert HTMLElement { [...] } returniere customElements.define( 'Hallo Welt', UnityComponent ); } Jetzt können Sie es öffnen und wie folgt nachsehen: In Chrome hat diese Schaltflächenkomponente ein rotes Rechteck mit dem Text: Hallo Welt! Mein Name ist Comandeer. Abrufen des SkriptinhaltsNun wurde eine einfache Button-Komponente implementiert. Jetzt kommt der schwierige Teil: das Hinzufügen der Verhaltensebene und das Anpassen des Inhalts innerhalb der Schaltfläche. In den obigen Schritten sollten wir den an die Schaltfläche übergebenen Inhalt verwenden, anstatt den Textinhalt der Schaltfläche im Komponentencode fest zu codieren. In ähnlicher Weise müssen wir auch Ereignislistener handhaben, die innerhalb von Komponenten gebunden sind. Hier verwenden wir eine Konvention ähnlich Vue.js, wie folgt: <Vorlage> […] </Vorlage> <Stil> […] </Stil> <Skript> Standard exportieren { // 1 Name: 'Hallo Welt', // 2 bei Klick() { // 3 alert( `Fass mich nicht an!` ); } } </Skript> Sie können davon ausgehen, dass der Inhalt innerhalb eines <script>-Tags innerhalb einer Komponente ein JavaScript-Modul ist, das Inhalte exportiert (1). Das von einem Modul exportierte Objekt enthält den Namen der Komponente (2) und eine Event-Listener-Methode, die mit „on..“ beginnt (3). Das sieht gut aus und außerhalb des Moduls wird nichts angezeigt (da Module in JavaScript nicht im globalen Bereich liegen). Hier gibt es ein Problem: Es gibt keinen Standard für die Handhabung von Objekten, die aus internen Modulen (die direkt im HTML-Dokument definiert sind) exportiert werden. Die Importanweisung geht davon aus, dass eine Modulkennung vorliegt und führt den Import entsprechend dieser Kennung durch. Am häufigsten handelt es sich um einen URL-Pfad zu einer Datei mit Code. Eine Komponente ist keine JS-Datei und hat keinen solchen Bezeichner. Das interne Modul hat keinen solchen Bezeichner. Bevor Sie aufgeben, können Sie einen super schmutzigen Trick anwenden. Es gibt für einen Browser mindestens zwei Möglichkeiten, einen Textabschnitt so zu behandeln, als wäre er eine Datei: Daten-URI und Objekt-URI. Es gibt auch einige Vorschläge zur Verwendung von Service Worker. Aber das erscheint mir hier etwas übertrieben. Daten-URI und Objekt-URIDaten-URI ist eine alte, primitive Methode. Es basiert darauf, den Dateiinhalt in eine URL zu konvertieren, unnötige Leerzeichen zu entfernen und dann alles mit Base64 zu kodieren. Angenommen, es gibt eine JavaScript-Datei mit folgendem Inhalt: Exportstandardwert ist wahr; Wie folgt in Daten-URI konvertiert: Daten:Anwendung/Javascript;Base64,ZXhwb3J0IGRlZmF1bHQgdHJ1ZTs= Sie können diese URI dann genauso importieren, wie Sie eine Datei importieren würden: Test aus „data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQgdHJ1ZTs=“ importieren; konsole.log( test ); Ein offensichtlicher Nachteil des Data-URI-Ansatzes besteht darin, dass die Länge der URL mit zunehmendem Inhalt der JavaScript-Datei sehr groß wird. Außerdem ist es sehr schwierig, Binärdaten in eine Daten-URI einzufügen. Es gibt jetzt also eine neue Art von Objekt-URI. Es leitet sich von mehreren Standards ab, darunter der File API und den <video>- und <audio>-Tags in HTML5. Der Zweck von Objekt-URIs ist einfach: Sie erstellen eine „Pseudodatei“ aus gegebenen Binärdaten und geben dabei einen eindeutigen URI im aktuellen Kontext an. Einfach ausgedrückt erstellt es eine Datei mit einem eindeutigen Namen im Speicher. Objekt-URI bietet alle Vorteile von Daten-URI (eine Möglichkeit zum Erstellen einer „Datei“), jedoch ohne dessen Nachteile (es spielt keine Rolle, auch wenn die Datei 100 MB groß ist). Objekt-URIs werden typischerweise aus Multimedia-Streams (wie etwa in <video>- oder <audio>-Kontexten) oder aus Dateien erstellt, die über Eingabe-[Typ=Datei]- und Drag-and-Drop-Mechanismen gesendet werden. Sie können sie auch mit den Klassen „File“ und „Blob“ manuell erstellen. In diesem Beispiel verwenden wir Bolb, um den Inhalt zuerst in ein Modul einzufügen und ihn dann in eine Objekt-URI umzuwandeln: const myJSFile = neuer Blob( [ 'export default true;' ], { Typ: 'Anwendung/Javascript' } ); const meinJSURL = URL.createObjectURL( meinJSFile ); console.log( meineJSURL ); // blob:https://blog.comandeer.pl/8e8fbd73-5505-470d-a797-dfb06ca71333 Dynamischer ImportEs gibt jedoch ein Problem: Die Importanweisung akzeptiert keine Variablen als Modulbezeichner. Dies bedeutet, dass es außer der Konvertierung eines Moduls in eine „Datei“ mit dieser Methode keine Möglichkeit gibt, es zu importieren. Gibt es noch keine Lösung? Nicht unbedingt. Dieses Problem besteht bereits seit längerem und kann mithilfe eines dynamischen Importmechanismus gelöst werden. Es ist Teil des ES2020-Standards und wurde in Firefox, Safari und Node.js 13.x implementiert. Die Verwendung einer Variablen als Bezeichner für ein dynamisch zu importierendes Modul ist kein Problem mehr: const myJSFile = neuer Blob( [ 'export default true;' ], { Typ: 'Anwendung/Javascript' } ); const meinJSURL = URL.createObjectURL( meinJSFile ); importiere( meineJSURL ).then( ( Modul ) => { konsole.log(modul.standard); // wahr }); Wie Sie dem obigen Code entnehmen können, kann der Befehl import() wie eine Methode verwendet werden. Er gibt ein Promise-Objekt zurück und das Modulobjekt wird in der then-Methode abgerufen. Sein Standardattribut enthält alle im Modul definierten exportierten Objekte. erreichenNachdem wir die Idee haben, können wir mit der Umsetzung beginnen. Fügen Sie eine Toolmethode hinzu, getSetting(). Rufen Sie es vor der Methode registerComponents() auf, um alle Informationen aus dem Skriptcode zu erhalten. Funktion getSettings({ Vorlage, Stil, Skript }) { zurückkehren { Vorlage, Stil, Skript }; } [...] Funktion loadComponent( URL ) { returniere fetchAndParse(URL).then(getSettings).then(registerComponent); } Jetzt gibt diese Methode alle übergebenen Parameter zurück. Konvertieren Sie den Skriptcode gemäß der oben beschriebenen Logik in eine Objekt-URI: const jsFile = neuer Blob( [ script.textContent ], { Typ: 'application/javascript' } ); const jsURL = URL.createObjectURL( jsFile ); Verwenden Sie als Nächstes „Import“, um das Modul zu laden und den Namen der Vorlage, des Stils und der Komponente zurückzugeben: return import( jsURL ).then( ( Modul ) => { zurückkehren { Name: Modul.Standard.Name, Vorlage, Stil } } ); Aus diesem Grund erhält registerComponent() immer noch 3 Argumente, jetzt jedoch den Namen anstelle des Skripts. Der richtige Code lautet wie folgt: Funktion registerComponent({ Vorlage, Stil, Name }) { Klasse UnityComponent erweitert HTMLElement { [...] } gibt customElements.define(Name, UnityComponent) zurück; } VerhaltensebeneDer Komponente fehlt noch eine letzte Ebene: die Verhaltensebene, die zur Verarbeitung von Ereignissen verwendet wird. Jetzt erhalten wir nur noch den Namen der Komponente in der Methode getSettings() und müssen außerdem den Ereignislistener abrufen. Es kann mit der Methode Object.entrie() abgerufen werden. Fügen Sie der Methode getSettings() den entsprechenden Code hinzu: Funktion getSettings({ Vorlage, Stil, Skript }) { [...] Funktion getListeners(Einstellungen) { // 1 const-Listener = {}; Object.entries( Einstellungen ).forEach( ( [ Einstellung, Wert ] ) => { // 3 wenn (Einstellung.startetMit('ein')) { // 4 listeners[ Einstellung[ 2 ].toLowerCase() + Einstellung.substr( 3 ) ] = Wert; // 5 } } ); Zuhörer zurückgeben; } return import( jsURL ).then( ( Modul ) => { const listeners = getListeners(modul.default); // 2 zurückkehren { Name: Modul.Standard.Name, Zuhörer, // 6 Vorlage, Stil } } ); } Jetzt wird die Methode etwas komplizierter. Eine neue Funktion getListeners() (1) wurde hinzugefügt, die die Ausgabe des Moduls an diesen Parameter übergibt. Verwenden Sie dann die Methode Object.entries() (3), um über die exportierten Module zu iterieren. Wenn das aktuelle Attribut mit „on“ (4) beginnt, handelt es sich um eine Listening-Funktion. Füge den Wert dieses Knotens (Listening-Funktion) zum Listener-Objekt hinzu und verwende setting[2].toLowerCase()+setting.substr(3) (5), um den Schlüsselwert zu erhalten. Der Schlüsselwert wird gebildet, indem das führende „on“ entfernt und der erste Buchstabe des folgenden „Click“ in Kleinbuchstaben umgewandelt wird (d. h., aus „onClick“ wird „click“ als Schlüsselwert). Übergeben Sie dann das Isteners-Objekt (6). Sie können die Methode [].reduce() anstelle der Methode [].forEach() verwenden, sodass Sie die Listener-Variable wie folgt weglassen können: Funktion getListeners( Einstellungen ) { return Object.entries( Einstellungen ).reduce( ( Listener, [ Einstellung, Wert ] ) => { wenn (Einstellung.startetMit('ein')) { Listener [Einstellung [2].toLowerCase() + Einstellung.substr (3)] = Wert; } Zuhörer zurückgeben; }, {} ); } Jetzt können Sie den Listener an die Klasse innerhalb der Komponente binden: Funktion registerComponent({ Vorlage, Stil, Name, Listener }) { // 1 Klasse UnityComponent erweitert HTMLElement { verbundenerCallback() { dies._upcast(); this._attachListeners(); // 2 } [...] _attachListeners() { Object.entries( listeners ).forEach( ( [ event, listener ] ) => { // 3 this.addEventListener( Ereignis, Listener, false ); // 4 } ); } } gibt customElements.define(Name, UnityComponent) zurück; } Der Listener-Methode wird ein Parameter hinzugefügt (1) und der Klasse wird eine neue Methode _attachListeners() hinzugefügt (2). Hier können wir erneut Object.entries() verwenden, um über die Listener zu iterieren (3) und sie an das Element zu binden (4). Wenn Sie schließlich auf die Komponente klicken, wird die Meldung „Berühren Sie mich nicht!“ angezeigt, wie unten dargestellt: Kompatibilitätsprobleme und andere ProblemeWie Sie sehen, dreht sich bei der Implementierung dieser Einzeldateikomponente die meiste Arbeit um die Unterstützung des Basisformulars. In vielen Teilen werden schmutzige Hacks verwendet (Verwendung von Object URI zum Laden von Modulen in ES, was ohne Browserunterstützung sinnlos ist). Glücklicherweise funktionieren alle Techniken in den gängigen Browsern, einschließlich Chrome, Firefox und Safari, einwandfrei. Trotzdem hat es Spaß gemacht, ein solches Projekt zu erstellen, das die Arbeit mit vielen Browsertechnologien und den neuesten Webstandards ermöglicht. Schließlich ist der Code für dieses Projekt online verfügbar. ZusammenfassenDies ist das Ende dieses Artikels über die Implementierung einer Einzeldateikomponente in Vue. Weitere relevante Inhalte zu Vue-Einzeldateikomponenten finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
>>: Pycharm2017 realisiert die Verbindung zwischen Python3.6 und MySQL
Dieser Artikel beschreibt, wie man das Linux-Syst...
Azure Container Registry ist ein verwalteter, ded...
Inhaltsverzeichnis Tabellendefinition - automatis...
Installationsvorschlag : Versuchen Sie, für die I...
Problembeschreibung: Wenn die Anzahl der asynchro...
1. Der Unterschied zwischen TEXT und BLOB Der ein...
Herunterladen: http://dev.mysql.com/downloads/mys...
Beispielvorgang für nicht festgeschriebenes Lesen...
In diesem Artikel finden Sie das Installations-Tu...
nginx (Engine x) ist ein leistungsstarker HTTP- u...
Bevor wir jQuery verwenden, um den Ein- und Ausbl...
Kürzlich erhielten wir von einem Kunden eine Bitt...
Vor Kurzem habe ich gelernt, React mit Three.js z...
1. Dynamische Komponenten <!DOCTYPE html> &...
Als ich mich bei MySQL anmeldete, wurde mir plötz...