Hallo Habr!
Mit jedem modernen Browser können Sie jetzt mit
ES6-Modulen arbeiten .
Auf den ersten Blick scheint dies eine völlig nutzlose Sache zu sein - schließlich verwenden wir alle Sammler, die Importe durch ihre internen Herausforderungen ersetzen. Wenn Sie sich jedoch mit den Spezifikationen befassen, stellt sich heraus, dass Sie dank dieser Spezifikationen eine separate Baugruppe für moderne Browser erstellen können.
Unter der Katze ist eine Geschichte darüber, wie ich die Größe der Anwendung unbeschadet alter Browser und meiner Nerven um 11% reduzieren konnte.
Merkmale von ES6-Modulen
ES6 Modules ist ein bekanntes und weit verbreitetes modulares System für alle:
import { someFunc } from 'path/to/helpers.js'
export function someFunc() { }
Um dieses modulare System in Browsern zu verwenden, müssen Sie jedem Skript-Tag den Modultyp hinzufügen. Ältere Browser erkennen, dass sich der Typ von Text / Javascript unterscheidet, und führen die Datei nicht als JavaScript aus.
<script type="module" src="/path/to/someFile.js"></script>
Die Spezifikation hat auch ein Nomodule-Attribut für Skript-Tags. Browser, die
ES6-Module unterstützen , ignorieren dieses Skript, und ältere Browser laden es herunter und führen es aus.
<script nomodule src="/path/to/someFileFallback.js"></script>
Es stellt sich heraus, dass Sie einfach zwei Assemblys erstellen können: die erste mit dem Modultyp für moderne Browser (Modern Build) und die andere mit dem Nomodul für alte Browser (Fallback Build):
<script type="module" src="/path/to/someFile.js"></script> <script nomodule src="/path/to/someFileFallback.js"></script>
Warum ist es notwendig?
Bevor wir ein Projekt an die Produktion senden, müssen wir:
- Fügen Sie Polyphile hinzu.
- Transponieren Sie modernen Code in einen älteren.
In meinen Projekten versuche ich, die maximale Anzahl von Browsern zu unterstützen, manchmal sogar
IE 10 . Daher besteht meine Liste von Polydateien aus grundlegenden Dingen wie es6.promise, es6.object.values usw. Browser mit
ES6-Modulen unterstützen jedoch alle ES6-Methoden und benötigen keine zusätzlichen Kilobyte Polyfills.
Die Transpilation hinterlässt auch deutliche Spuren in der Dateigröße: babel / preset-env verwendet 25 Transformatoren, um die meisten Browser abzudecken, von denen jeder die Größe des Codes erhöht. Gleichzeitig wird für Browser mit Unterstützung für
ES6-Module die Anzahl der Transformatoren auf 9 reduziert.
In der Assembly für moderne Browser können wir also unnötige Polydateien entfernen und die Anzahl der Transformatoren reduzieren, was die Größe der resultierenden Dateien stark beeinflusst!
So fügen Sie Polydateien hinzu
Bevor Sie Modern Build für moderne Browser vorbereiten, sollten Sie erwähnen, wie ich dem Projekt Polyfills hinzufüge.
In der Regel verwenden Projekte Core-Js, um alle möglichen Polyfills hinzuzufügen.
Natürlich möchten Sie nicht alle 88 KByte Polydateien aus dieser Bibliothek, sondern nur diejenigen, die für Ihre Browserliste benötigt werden. Diese Funktion ist mit babel / preset-env und der Option useBuiltIns verfügbar. Wenn Sie den Eintrag festlegen, wird der Import von core-js durch den Import der einzelnen Module ersetzt, die Ihre Browser benötigen:
module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'entry', }] ], };
import 'core-js';
import "core-js/modules/es6.array.copy-within"; import "core-js/modules/es6.array.fill"; import "core-js/modules/es6.array.find";
Aber durch eine solche Transformation haben wir nur einen Teil der unnötigen, sehr alten Polyphilen beseitigt. Wir haben immer noch Polyphile für TypedArray, WeakMap und andere seltsame Dinge, die im Projekt nie verwendet werden.
Um dieses Problem vollständig zu lösen, habe ich für die Option useBuiltIns den Wert auf usage festgelegt. In der Kompilierungsphase analysiert babel / preset-env Dateien auf die Verwendung von Funktionen, die in den ausgewählten Browsern nicht verfügbar sind, und fügt ihnen Polydateien hinzu:
module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', }] ], };
function sortStrings(strings) { return strings.sort(); } function createResolvedPromise() { return Promise.resolve(); }
import "core-js/modules/es6.array.sort"; import "core-js/modules/es6.promise"; function sortStrings(strings) { return strings.sort(); } function createResolvedPromise() { return Promise.resolve(); }
Im obigen Beispiel hat babel / preset-env der Sortierfunktion ein Polyphil hinzugefügt. In JavaScript können Sie nicht herausfinden, welcher Objekttyp an die Funktion übergeben wird. Es handelt sich um ein Array oder ein Klassenobjekt mit der Sortierfunktion. Babel / preset-env wählt jedoch das Worst-Case-Szenario für sich selbst aus und fügt eine Polydatei ein.
Situationen, in denen babel / preset-env falsch ist, treten ständig auf. Um unnötige Polyphile zu entfernen, überprüfen Sie von Zeit zu Zeit, welche davon Sie importieren, und löschen Sie unnötige mit der Ausschlussoption:
module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage',
Ich berücksichtige das Regenerator-Laufzeitmodul nicht, da ich Fast-Async verwende ( und ich rate jedem ).Erstellen Sie einen modernen Build
Lassen Sie uns Modern Build einrichten.
Stellen Sie sicher, dass das Projekt eine Browserlistendatei enthält, in der alle erforderlichen Browser beschrieben werden:
/* .browserslistrc */ > 0.5% IE 10
Fügen Sie während des Builds die Umgebungsvariable BROWSERS_ENV hinzu, die die Werte fallback (für Fallback Build) und modern (für Modern Build) annehmen kann:
/* package.json */ { "scripts": { /* ... */ "build": "NODE_ENV=production webpack /.../", "build:fallback": "BROWSERS_ENV=fallback npm run build", "build:modern": "BROWSERS_ENV=modern npm run build" }, /* ... */ }
Ändern Sie nun die Konfiguration von babel / preset-env. Um unterstützte Browser in der Voreinstellung anzugeben, gibt es eine Option Ziele. Sie hat eine spezielle Abkürzung - esmodules. Bei Verwendung ersetzt babel / preset-env automatisch Browser, die
ES6-Module unterstützen .
const isModern = process.env.BROWSERS_ENV === 'modern'; module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage',
Babel / preset-env wird die ganze Arbeit für uns weiter erledigen: Es werden nur die notwendigen Polyphilen und Transformationen ausgewählt.
Jetzt können wir ein Projekt für moderne oder alte Browser erstellen, nur einen Befehl von der Konsole aus!
Bind Modern und Fallback Build
Der letzte Schritt besteht darin, Modern und Fallback Builds in einem zu kombinieren.
Ich plane eine solche Projektstruktur zu erstellen:
In index.html gibt es Links zu den erforderlichen Javascript-Dateien von beiden Assemblys:
/* index.html */ <html> <head> </head> <body> <script type="module" src="/modern/js/app.540601d23b6d03413d5b.js"></script> <script nomodule src="/fallback/js/app.4d03e1af64f68111703e.js"></script> </body> </html>
Dieser Schritt kann in drei Teile unterteilt werden:
- Build Modern und Fallback Build in verschiedenen Verzeichnissen.
- Informationen zu den Pfaden zu den erforderlichen Javascript-Dateien abrufen.
- Erstellen von index.html mit Links zu allen Javascript-Dateien.
Fangen wir an!
Build Modern und Fallback Build in verschiedenen Verzeichnissen
Lassen Sie uns zunächst den einfachsten Schritt machen: Wir werden Modern und Fallback Build in verschiedenen Verzeichnissen im dist-Verzeichnis sammeln.
Es ist einfach unmöglich, das gewünschte Verzeichnis für output.path anzugeben, da das Webpack Pfade zu Dateien relativ zum dist-Verzeichnis benötigt (index.html befindet sich in diesem Verzeichnis, und alle anderen Abhängigkeiten werden relativ dazu herausgepumpt).
Erstellen Sie eine spezielle Funktion zum Generieren von Dateipfaden:
const path = require('path'); const isModern = process.env.BROWSERS_ENV === 'modern'; const prefix = isModern ? 'modern' : 'fallback'; module.exports = relativePath => ( path.join(prefix, relativePath) );
const getFilePath = require('path/to/getFilePath'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'production', output: { path: 'dist', filename: getFilePath('js/[name].[contenthash].js'), }, plugins: [ new MiniCssExtractPlugin({ filename: getFilePath('css/[name].[contenthash].css'), }), ], }
Das Projekt begann sich in verschiedenen Verzeichnissen für Modern und Fallback Build zu sammeln.
Informationen zu den Pfaden zu den erforderlichen Javascript-Dateien abrufen
Um Informationen zu den gesammelten Dateien zu erhalten, verbinden Sie das Webpack-Manifest-Plugin. Am Ende der Assembly fügt er eine manifest.json-Datei mit Daten zu den Pfaden zu den Dateien hinzu:
const getFilePath = require('path/to/getFilePath'); const WebpackManifestPlugin = require('webpack-manifest-plugin'); module.exports = { mode: 'production', plugins: [ new WebpackManifestPlugin({ fileName: getFilePath('manifest.json'), }), ], }
Jetzt haben wir Informationen zu den gesammelten Dateien:
{ "app.js": "/fallback/js/app.4d03e1af64f68111703e.js", }
Erstellen von index.html mit Links zu allen Javascript-Dateien
Sie müssen nur noch index.html hinzufügen und die Pfade zu den erforderlichen Dateien einfügen.
Um die HTML-Datei zu generieren, werde ich das HTML-Webpack-Plugin während Modern Build verwenden. Das HTML-Webpack-Plugin fügt die Pfade zu den modernen Dateien selbst ein, und ich erhalte die Pfade zu den Fallback-Dateien aus der im vorherigen Schritt erstellten Datei und füge sie mit einem kleinen Webpack-Plugin in den HTML-Code ein:
const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModernBuildPlugin = require('path/to/ModernBuildPlugin'); module.exports = { mode: 'production', plugins: [ ...(isModern ? [
Update package.json:
/* package.json */ { "scripts": { /* ... */ "build:full": "npm run build:fallback && npm run build:modern" }, /* ... */ }
Mit dem Befehl npm run build: full erstellen wir eine HTML-Datei mit Modern und Fallback Build. Jeder Browser erhält nun das JavaScript, das er ausführen kann.
Fügen Sie Ihrer Anwendung Modern Build hinzu
Um meine Lösung an etwas Realem zu testen, habe ich sie in eines meiner Projekte eingebaut. Das Einrichten der Konfiguration dauerte weniger als eine Stunde, und die Größe der JavaScript-Dateien verringerte sich um 11%. Tolles Ergebnis mit einer einfachen Implementierung.
Vielen Dank, dass Sie den Artikel bis zum Ende gelesen haben!
Verwendete Materialien