Details zur Bündelung statischer Ressourcen ohne JavaScript

Details zur Bündelung statischer Ressourcen ohne JavaScript

Dieser Artikel wurde von https://web.dev/bundling-non-js-resources/ übersetzt. Der Originaltext wurde nicht verändert.

Angenommen, Sie entwickeln eine Webanwendung. In diesem Fall haben Sie es wahrscheinlich nicht nur mit JavaScript-Modulen zu tun, sondern auch mit verschiedenen anderen Ressourcen – Web Workers (die ebenfalls JavaScript sind, aber über ein eigenes Build-Abhängigkeitsdiagramm verfügen), Bildern, CSS, Schriftarten, WebAssembly Modulen und so weiter.

Eine Möglichkeit zum Laden statischer Ressourcen besteht darin, sie direkt im HTML zu referenzieren. Normalerweise sind sie jedoch logisch mit anderen wiederverwendbaren Komponenten gekoppelt. Beispielsweise ist CSS eines benutzerdefinierten Dropdown-Menüs an seinen JavaScript Teil gebunden, das Symbolbild an die Symbolleistenkomponente und WebAssembly Modul an seinen JavaScript Kleber. In diesen Fällen ist es oft bequemer und schneller, die Assets direkt aus ihren JavaScript Modulen zu referenzieren und sie dynamisch zu laden, wenn die entsprechende Komponente geladen wird.

Die meisten Build-Systeme für größere Projekte führen jedoch zusätzliche Optimierungen und Neuorganisationen des Inhalts durch, beispielsweise Bündelung und minimize . Ein Build-System kann keinen Code ausführen und das Ergebnis vorhersagen, und es gibt keinen Grund, jeden möglichen String in JavaScript zu durchlaufen und zu bestimmen, ob es sich um eine Ressourcen-URL handelt. Wie können wir also dafür sorgen, dass sie die von JavaScript Komponenten geladenen dynamischen Ressourcen „sehen“ und in das Build-Produkt einbinden?

1. Benutzerdefinierter Import in Verpackungstools

Ein gängiger Ansatz besteht darin, die vorhandene statische Importsyntax zu nutzen. Einige Bundler erkennen das Format automatisch anhand der Dateierweiterung, während andere Plugins die Verwendung eines benutzerdefinierten URL Scheme gestatten, wie im folgenden Beispiel:

// Normales JavaScript importimport { loadImg } von './utils.js';

// Spezieller „URL-Import“ – statische Ressourcen importieren imageUrl von „asset-url:./image.png“;
importiere wasmUrl aus „asset-url:./module.wasm“;
importiere workerUrl von „js-url:./worker.js“;

Bild laden(Bild-URL);
: WebAssembly.instantiateStreaming(fetch(wasmUrl));
neuer Worker(workerUrl);

Wenn ein Bundler-Plugin einen Import mit einer erkannten Erweiterung oder einem erkannten URL Scheme sieht (asset-url: und js-url: in den obigen Beispielen), fügt es die referenzierte Ressource dem Build-Diagramm hinzu, kopiert sie an das endgültige Ziel, führt dem Ressourcentyp entsprechende Optimierungen durch und gibt die endgültige URL zur Verwendung zur Laufzeit zurück.

Die Vorteile dieses Ansatzes sind: Wiederverwendung JavaScript Importsyntax und Gewährleistung, dass alle URLs statische relative Pfade sind, was es dem Build-System erleichtert, solche Abhängigkeiten zu finden.

Dies hat jedoch einen offensichtlichen Nachteil: Ein solcher Code funktioniert nicht direkt in Browsern, da Browser nicht wissen, wie sie mit diesen benutzerdefinierten Importschemata oder Erweiterungen umgehen sollen. Wenn Sie Ihren gesamten Code kontrollieren und bei der Entwicklung ohnehin auf einen Bundler angewiesen sind, klingt das natürlich großartig. Um den Aufwand zu reduzieren, wird es jedoch immer üblicher, JavaScript Module direkt im Browser zu verwenden (zumindest während der Entwicklung). Für eine kleine demo ist möglicherweise selbst in der Produktion überhaupt kein Bundler erforderlich.

2. Allgemeine Importsyntax in Browsern und Bundlern

Wenn Sie eine wiederverwendbare Komponente entwickeln, möchten Sie, dass sie in jeder Umgebung funktioniert, unabhängig davon, ob sie direkt im Browser verwendet oder als Teil einer größeren Anwendung vorgefertigt wird. Die meisten modernen Bundler akzeptieren die folgende Importsyntax für JavaScript-Module:

new URL('./relative-path', import.meta.url)

Es sieht aus wie eine spezielle Syntax, ist aber tatsächlich ein gültiger JavaScript Ausdruck, der direkt im Browser verwendet und auch von Bundlern statisch erkannt und verarbeitet werden kann.

Mit dieser Syntax kann das vorherige Beispiel wie folgt umgeschrieben werden:

// regulärer JavaScript-Import
importiere { loadImg } aus './utils.js';
loadImg(neue URL('./image.png', import.meta.url));
WebAssembly.instantiateStreaming(
  holen(neue URL('./module.wasm', import.meta.url)),
  { /* … */ }
);
neuer Worker (neue URL ('./worker.js', import.meta.url));


Lassen Sie uns analysieren, wie es funktioniert: new URL(...) Konstruktor löst die URL auf, die der relativen URL im ersten Parameter entspricht, basierend auf der absoluten URL im zweiten Parameter. In unserem Fall ist das zweite Argument import.meta.url [1] , also die URL des aktuellen JavaScript Moduls. Das erste Argument kann also ein beliebiger Pfad relativ dazu sein.

Es hat ähnliche Vor- und Nachteile wie der dynamische Import. Während es möglich ist, Inhalte mit import(...) zu importieren, etwa mit import (someUrl), behandeln Bundler Importe mit statischen URL import ('./some-static-url.js') speziell: als eine Möglichkeit, bekannte Abhängigkeiten zur Kompilierzeit vorzuverarbeiten, indem der Code in Blöcke aufgeteilt und dynamisch geladen wird.

Ebenso können Sie new URL (...) verwenden, wie in new URL (relativeUrl, customAbsoluteBase)“. Die Syntax new URL ('...', import.meta.url) weist den Bundler jedoch ausdrücklich an, die Abhängigkeit vorzuverarbeiten und sie mit dem Haupt-JavaScript-Asset zu bündeln.

3. Mehrdeutige relative URLs

Sie fragen sich vielleicht, warum Bundler andere gängige Syntax nicht erkennen können, beispielsweise „ fetch ('./module.wasm')“ ohne new URL Wrapper?

Der Grund hierfür liegt darin, dass sich im Gegensatz zum Schlüsselwort import “ alle dynamischen Anforderungen auf das Dokument selbst beziehen und nicht auf die aktuell analysierte JavaScript-Datei. Nehmen wir an , wir haben die folgende Struktur:

Datei:

<script src="src/main.js" Typ="Modul"></script>


Quelle/

Haupt-JS

modul.wasm

Wenn Sie module.wasm aus main.js laden möchten, besteht Ihr erster Instinkt möglicherweise darin, eine relative Pfadreferenz wie fetch('./module.wasm') zu verwenden.

Allerdings kennt fetch die URL der JavaScript Datei nicht, die es ausführt. Stattdessen löst es die URL relativ zum Dokument auf. Daher versucht fetch ('./module.wasm') letztendlich, http://example.com/module.wasm statt der erwarteten http://example.com/src/module.wasm zu laden, was zu einem Fehler führt (oder im schlimmsten Fall dazu, dass unbemerkt eine andere Ressource als erwartet geladen wird).

Indem Sie relative URLs in new URL ('...', import.meta.url) einschließen, können Sie dieses Problem vermeiden und sicherstellen, dass jede angegebene URL relativ zur URL des aktuellen JavaScript-Moduls ( import.meta.url ) aufgelöst wird, bevor sie an Loader übergeben wird.

Durch einfaches Ersetzen von fetch ('./module.wasm') durch fetch (new URL('./module.wasm', import.meta.url)) wird das gewünschte WebAssembly Modul erfolgreich geladen und Bundlern wird gleichzeitig eine zuverlässige Möglichkeit geboten, diese relativen Pfade zur Build-Zeit zu finden.

4. Unterstützung in der Toolchain

4.1 Verpackungswerkzeuge

Die folgenden Bundler unterstützen bereits die neue URL-Syntax:

  • Webpack v5
  • Rollup (unterstützt über Plugins: @web/rollup-plugin-import-meta-assets für allgemeine Assets und @surma/rollup-plugin-off-main- thread für Worker.)
  • Parcel v2 (beta)
  • Vite

5. WebAssembly

Wenn Sie WebAssembly verwenden, laden Sie Wasm -Module normalerweise nicht manuell, sondern importieren stattdessen von der Toolchain ausgegebenen JavaScript Glue-Code. Die folgende Toolchain kann die neue URL(...)-Syntax für Sie generieren:

5.1 C/C++ kompiliert mit Emscripten

Wenn wir die Emscripten -Toolchain verwenden, können wir sie mit der folgenden Option anweisen, ES6-Modul-Glue-Code anstelle von normalem JS-Code auszugeben:

$ emcc Eingabe.cpp -o Ausgabe.mjs
## Wenn Sie die mjs-Erweiterung nicht verwenden möchten:
$ emcc input.cpp -o output.js -s EXPORT_ES6


Wenn diese Option verwendet wird, nutzt der Ausgabe-Glue-Code new URL Syntax (..., import.meta.url), sodass Bundler die relevanten Wasm-Dateien automatisch finden können.

Durch Hinzufügen des Parameters -pthread kann diese Syntax auch die Kompilierung von WebAssembly Threads unterstützen.

$ emcc input.cpp -o output.mjs -pthread
## Wenn Sie die mjs-Erweiterung nicht verwenden möchten:
$ emcc input.cpp -o output.js -s EXPORT_ES6 -pthread


In diesem Fall wird der generierte Web Worker auf die gleiche Weise referenziert und von Bundlern und Browsern korrekt geladen.

5.2 Rust kompiliert mit wasm-pack / wasm-bindgen

wasm-pack --WebAssembly Die wichtigste Rust-Toolchain für WebAssembly, ebenfalls mit mehreren Ausgabemodi.

Standardmäßig wird ein JavaScript Modul ausgegeben, das auf WebAssembly ESM Integrationsvorschlag basiert. Zum Zeitpunkt des Verfassens dieses Beitrags ist dieser Vorschlag noch experimentell und die Ausgabe funktioniert nur in Verbindung mit Webpack .

Alternativ können wir wasm-pack mit dem Argument -target web auffordern, ein browserkompatibles ES6-Modul auszugeben:

$ wasm-pack build --Zielweb


Die Ausgabe verwendet die oben beschriebene neue URL-Syntax (..., import.meta.url) und Wasm-Dateien werden automatisch vom Bundler erkannt.

Wenn Sie WebAssembly Threads von Rust verwenden möchten, ist es etwas komplizierter. Weitere Informationen finden Sie im entsprechenden Abschnitt des Leitfadens [13].

Kurz gesagt, Sie können nicht beliebige Threading-APIs verwenden, aber wenn Sie Rayon [14] verwenden, können Sie den wasm-bingen-rayon [15]-Adapter ausprobieren, der Worker generieren kann, die im Web ausgeführt werden können. Der von wasm-bindgen-rayon verwendete JavaScript-Glue enthält auch die [16] neue URL (...)-Syntax, sodass Workers auch von Bundlern entdeckt und importiert werden können.

6. Zukünftige Importmethoden

6.1 importiere.meta.resolve

Eine mögliche zukünftige Verbesserung ist eine spezielle import.meta.resolve(...) -Syntax. Dadurch kann Inhalt relativ zum aktuellen Modul direkter aufgelöst werden, ohne dass zusätzliche Argumente erforderlich sind.

// Aktuelle Syntax neue URL('...', import.meta.url)

// Zukünftige Syntax wartet auf import.meta.resolve('...')

Es lässt sich auch besser in import maps und benutzerdefinierte Resolver integrieren, da es vom selben Modulauflösungssystem wie import verarbeitet wird. Dies ist auch ein zuverlässigeres Signal für Bundler, da es sich um eine statische Syntax handelt, die nicht auf Laufzeit-APIs wie URLs angewiesen ist.

import.meta.resolve wurde in Node.js als experimentelle Funktion implementiert, aber es gibt noch einige ungeklärte Fragen dazu, wie es im Web funktionieren soll.

6.2 Import-Assertionen

Import-Assertions sind eine neue Funktion, die den Import von Typen außerhalb von ECMAScript-Modulen ermöglicht, obwohl derzeit nur JSON-Typen unterstützt werden.

foo.json

{ "Antwort": 42 }


Haupt.mjs

importiere json aus './foo.json' assert { Typ: 'json' };
konsole.log(json.antwort); // 42


(Anmerkung des Übersetzers: Es gibt auch einige interessante Informationen zu dieser weniger intuitiven Syntaxwahl https://github.com/tc39/proposal-import-assertions/issues/12)

Sie können auch von Bundlern verwendet werden und die Szenarien ersetzen, die derzeit von der neuen URL-Syntax unterstützt werden, aber die Typen in den Importbehauptungen müssen einzeln unterstützt werden. Derzeit wird nur JSON unterstützt. CSS Module kommen bald, aber andere Arten von Ressourcenimporten benötigen noch eine allgemeinere Lösung.

Weitere Informationen zu dieser Funktion finden Sie in der Funktionserklärung auf v8.dev [19].

7. Zusammenfassung

Wie Sie sehen, gibt es verschiedene Möglichkeiten, Nicht- JavaScript -Ressourcen in das Web einzubinden. Sie alle haben jedoch ihre eigenen Vor- und Nachteile und keine davon funktioniert in allen Toolchains gleichzeitig. Einige zukünftige Vorschläge ermöglichen uns möglicherweise den Import dieser Ressourcen mit einer speziellen Syntax, aber so weit sind wir noch nicht.

Bis dieser Tag kommt, ist new URL Syntax (..., import.meta.url) die vielversprechendste Lösung und funktioniert bereits heute in Browsern, verschiedenen Bundlern und WebAssembly -Toolchains.

Dies ist das Ende dieses Artikels zum Verpacken von statischen Ressourcen ohne JavaScript. Weitere relevante Inhalte zum Verpacken von statischen Ressourcen ohne JavaScript finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den verwandten Artikeln weiter unten. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

8. Referenzen

[1]; import.meta.url: https://v8.dev/features/modules#import-meta

[2]; Dynamischer Import: https://v8.dev/features/dynamic-import

[3]: Code-Splitting: https://web.dev/reduce-javascript-payloads-with-code-splitting/

[4]: Webpack v5: https://webpack.js.org/guides/asset-modules/#url-assets

[5]: Rollup: https://rollupjs.org/

[6]: @web/rollup-plugin-import-meta-assets: https://modern-web.dev/docs/building/rollup-plugin-import-meta-assets/

[7]: @surma/rollup-plugin-off-main-thread: https://github.com/surma/rollup-plugin-off-main-thread

[8]: Parcel v2 (beta): https://v2.parceljs.org/languages/javascript/#url-dependencies

[9]: Vite: https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url

[10]: WebAssembly: https://web.dev/webassembly-threads/#c

[11]: wasm-pack: https://github.com/rustwasm/wasm-pack

[12]: WebAssembly ESM-Integrationsvorschlag: https://github.com/WebAssembly/esm-integration

[13]: Entsprechender Teil: https://web.dev/webassembly-threads/#rust

[14]: Rayon: https://github.com/rayon-rs/rayon

[15]: wasm-bindgen-rayon: https://github.com/GoogleChromeLabs/wasm-bindgen-rayon

[16]: Ebenfalls enthalten: https://github.com/GoogleChromeLabs/wasm-bindgen-rayon/blob/4cd0666d2089886d6e8731de2371e7210f848c5d/demo/index.js#L26

[17]: Experimentelle Funktion: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent

[18]: Es gibt noch einige ungelöste Probleme: https://github.com/WICG/import-maps/issues/79

[19]: Funktionserklärung auf v8.dev: https://v8.dev/features/import-assertions

Das könnte Sie auch interessieren:
  • Statische Ressourcen und Verpackungsvorgänge von Nuxt.js

<<:  Einführung in gängige XHTML-Tags

>>:  So erstellen Sie eine MySQL-Datenbank und unterstützen chinesische Schriftzeichen

Artikel empfehlen

Javascript-Betriebsmechanismus „Event Loop“

Inhaltsverzeichnis 1. Vier Konzepte 1. JavaScript...

Axios storniert Anfragen und vermeidet doppelte Anfragen

Inhaltsverzeichnis Herkunft Status Quo Anfrage ab...

MySQL Workbench herunterladen und verwenden Tutorial detaillierte Erklärung

1. MySQL Workbench herunterladen Workbench ist ei...

5 äußerst nützliche Open-Source-Docker-Tools, die dringend empfohlen werden

Einführung Die Docker-Community hat viele Open-So...

Häufig verwendete höherwertige Funktionen und umfassende Beispiele in Vue

1. Häufig verwendete höherwertige Funktionen von ...

So implementieren Sie eine steuerbare gepunktete Linie mit CSS

Vorwort Die Verwendung von CSS zum Generieren gep...

Beispielcode zur Implementierung des Aushöhlungseffekts mit CSS

Wirkungsprinzip Verwenden Sie hauptsächlich CSS-F...

Analyse verschiedener Fehler bei Sortierregeln für MySQL-Tabellen

Der folgende Fehler wird gemeldet, wenn MySQL meh...

jQuery implementiert dynamische Tag-Ereignisse

In diesem Artikel wird der spezifische Code von j...

Zusammenfassung der HTML-Hack-Tags im IE-Browser

Code kopieren Der Code lautet wie folgt: <!--[...

So verwenden Sie Javascript zum Erstellen einfacher Algorithmen

Inhaltsverzeichnis 1 Frage 2 Methoden 3 Experimen...