Publiez, livrez et installez du code JavaScript moderne pour des applications plus rapides

Améliorez les performances en activant les dépendances et la sortie JavaScript modernes.

Plus de 90% des navigateurs sont capables d'exécuter du code JavaScript moderne, mais la prévalence de l'ancien JavaScript demeure une importante source de problèmes de performances sur le Web aujourd'hui.

JavaScript moderne

Le code JavaScript moderne n'est pas considéré comme du code écrit dans un langage ECMAScript spécifique. de la spécification, mais dans une syntaxe compatible avec tous les outils des navigateurs. Les navigateurs Web modernes tels que Chrome, Edge, Firefox et Safari constituent pour plus de 90% du marché des navigateurs. différents navigateurs qui s'appuient sur les mêmes moteurs de rendu sous-jacents constituent une 5 % supplémentaires. Cela signifie que 95% du trafic Web mondial provient des navigateurs compatibles avec les fonctionnalités de langage JavaScript les plus utilisées depuis les 10 dernières années années, dont:

  • Classes (ES2015)
  • Fonctions de flèche (ES2015)
  • Générateurs (ES2015)
  • Portée des blocs (ES2015)
  • Déstructuration (ES2015)
  • Paramètres de repos et de répartition (ES2015)
  • Raccourci de l'objet (ES2015)
  • Async/await (ES2017)

Les fonctionnalités des versions plus récentes des spécifications de langage ont généralement moins une compatibilité cohérente avec les navigateurs récents. Par exemple, ES2020 et ES2021 fonctionnalités ne sont prises en charge que par 70% des navigateurs sur le marché des navigateurs, toujours la majorité mais pas suffisamment pour pouvoir utiliser directement ces fonctionnalités en toute sécurité. Ce signifie que, bien que le terme "moderne" JavaScript est une cible mouvante, ES2017 a le plus large éventail de navigateurs compatibles tout en incluant la plupart des fonctionnalités de syntaxe modernes couramment utilisées. En d'autres termes, ES2017 est aujourd'hui la syntaxe qui se rapproche le plus de la syntaxe moderne.

Ancien JavaScript

L'ancien code JavaScript est un code qui évite l'utilisation de tous les langages ci-dessus. caractéristiques. La plupart des développeurs écrivent leur code source en utilisant une syntaxe moderne, mais Tout compiler avec l'ancienne syntaxe pour une meilleure prise en charge des navigateurs. Compilation à l'ancienne syntaxe augmente la compatibilité des navigateurs, mais cela entraîne souvent plus petite que nous ne le pensons. Dans de nombreux cas, le soutien passe d'environ 95% à 98% tout en encourant un coût important:

  • JavaScript ancien est généralement 20% plus volumineux et plus lent que un code moderne équivalent. Souvent, les défauts d'outils et les erreurs de configuration l'écart est encore plus grand.

  • Les bibliothèques installées représentent jusqu'à 90% de la production habituelle Code JavaScript. Le code de bibliothèque entraîne un code JavaScript ancien encore plus élevé surcoût lié au polyfill et à la duplication de l'outil d'aide qui pourraient être évités en publiant du code moderne.

Modern JavaScript sur npm

Récemment, Node.js a standardisé un champ "exports" pour définir Points d'entrée d'un package:

{
  "exports": "./index.js"
}

Les modules référencés par le champ "exports" impliquent une version de nœud d'au moins 12.8, compatible avec ES2019. Cela signifie que tout module référencé à l'aide du Le champ "exports" peut être écrit en JavaScript moderne. Les clients du package doivent supposer que les modules dont le champ "exports" contient du code moderne et transpiler si nécessaires.

Moderne uniquement

Si vous souhaitez publier un package avec du code moderne et le laisser de gérer la transpilation lorsqu'il l'utilise comme dépendance. Utilisez uniquement "exports".

{
  "name": "foo",
  "exports": "./modern.js"
}

Moderne avec une ancienne création de remplacement

Utilisez le champ "exports" avec "main" pour publier votre package utilisant du code moderne, mais aussi un remplacement ES5 + CommonJS pour les anciennes versions des navigateurs.

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs"
}

Moderne avec une ancienne solution de remplacement et des optimisations de bundler ESM

En plus de définir un point d'entrée CommonJS de remplacement, le champ "module" permet peut renvoyer vers un ancien bundle de remplacement similaire, mais qui utilise Syntaxe du module JavaScript (import et export).

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs",
  "module": "./module.js"
}

De nombreux bundlers, tels que webpack et Rollup, utilisent ce champ pour profiter fonctionnalités du module et activer trees tremblement d'arbre. Il s'agit toujours d'un ancien bundle qui ne contient aucun code moderne en dehors de import/export. Utilisez donc cette approche pour intégrer du code moderne avec une une ancienne création de remplacement qui est toujours optimisée pour le regroupement.

JavaScript moderne dans les applications

Les dépendances tierces constituent la grande majorité de la production classique Code JavaScript dans les applications Web. Bien que les dépendances npm aient historiquement publiée sous la forme d'une ancienne syntaxe ES5, cette hypothèse n'est plus sûre et risque d'entraîner des mises à jour de dépendances, ce qui interromprait la prise en charge du navigateur dans votre application.

Avec un nombre croissant de packages npm à migrer vers le langage JavaScript moderne, il est important de s'assurer que les outils de compilation sont configurés pour les gérer. Il y a un il y a de fortes chances que certains des packages npm dont vous dépendez utilisent déjà et des fonctionnalités de langage. Il existe un certain nombre d'options permettant d'utiliser du code moderne à partir de npm sans interrompre votre application dans les anciens navigateurs, l'idée est que le système de compilation transpile les dépendances en une même syntaxe comme code source.

Webpack

Depuis webpack 5, il est possible de configurer la syntaxe qu'il utilisera lors de la génération de code pour les bundles et les modules. Cela ne transpire pas ou des dépendances, elle n'affecte que la "colle" généré par Webpack. Pour spécifier la cible de compatibilité du navigateur, ajoutez un configuration navigateurslist à votre projet, ou directement dans votre configuration webpack:

module.exports = {
  target: ['web', 'es2017'],
};

Vous pouvez aussi configurer webpack pour générer des bundles optimisés omettre les fonctions de wrapper inutiles lors du ciblage d'un module ES moderne environnement. Cela configure également webpack pour charger des bundles de fractionnement de code à l'aide de <script type="module">

module.exports = {
  target: ['web', 'es2017'],
  output: {
    module: true,
  },
  experiments: {
    outputModule: true,
  },
};

De nombreux plug-ins webpack sont disponibles. Ils permettent compiler et diffuser du code JavaScript moderne tout en conservant la compatibilité avec les anciens navigateurs ; tels que les plug-ins Optimize et BabelEsmPlugin.

Plug-in Optimize

Le plug-in Optimize est un webpack Plug-in qui transforme le code groupé final du code JavaScript moderne en ancien code JavaScript au lieu de chaque fichier source individuel. Il s'agit d'une configuration autonome qui permet votre configuration webpack pour supposer que tout est en JavaScript moderne, sans un embranchement spécial pour plusieurs sorties ou syntaxes.

Étant donné que le plug-in Optimize fonctionne sur des bundles et non sur des modules individuels, il traite le code de votre application et vos dépendances de manière égale. Ainsi, vous pouvez utiliser des dépendances JavaScript modernes à partir de npm, car leur code sera regroupées et transcompilées dans la syntaxe correcte. Elle peut également être plus rapide que les solutions traditionnelles qui impliquent deux étapes de compilation, tout en générant des groupes distincts pour les navigateurs modernes et anciens. Les deux groupes d'offres sont conçu pour être chargé à l'aide modèle module/nomodule.

// webpack.config.js
const OptimizePlugin = require('optimize-plugin');

module.exports = {
  // ...
  plugins: [new OptimizePlugin()],
};

Optimize Plugin peut être plus rapide et plus efficace que le webpack personnalisé qui regroupent généralement le code moderne et l'ancien code séparément. Il gère également l'exécution de Babel à votre place et réduit des groupes à l'aide de Terser, avec des paramètres optimaux distincts pour les sorties modernes et anciennes. Enfin, les polyfills nécessaires au fonctionnement les anciens bundles sont extraits dans un script dédié et ne sont donc jamais dupliqués ou chargés inutilement dans les navigateurs plus récents.

Comparaison: transpiler les modules sources deux fois et transpiler les groupes générés.

BabelEsmPlugin

BabelEsmPlugin est un webpack qui fonctionne avec @babel/preset-env pour générer des versions modernes de bundles existants afin de transmettre du code moins transpilé à les navigateurs les plus récents. Il s'agit de la solution prête à l'emploi la plus populaire module/nomodule, utilisé par Next.js et CLI Preact.

// webpack.config.js
const BabelEsmPlugin = require('babel-esm-plugin');

module.exports = {
  //...
  module: {
    rules: [
      // your existing babel-loader configuration:
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
  plugins: [new BabelEsmPlugin()],
};

BabelEsmPlugin accepte un large éventail de configurations webpack, car il deux versions distinctes de votre application. Compiler deux fois peut prendre pour les applications volumineuses, mais cette technique permet BabelEsmPlugin pour une intégration parfaite dans les configurations Webpack existantes et en fait l'une des options les plus pratiques disponibles.

Configurer babel-loader pour transpiler node_modules

Si vous utilisez babel-loader sans l'un des deux plug-ins précédents, une étape importante est requise pour utiliser npm JavaScript moderne modules. Définir deux configurations babel-loader distinctes permet pour compiler automatiquement les fonctionnalités en langage moderne de node_modules afin de ES2017, tout en transpilant votre propre code first party avec l'API Babel les plug-ins et les préréglages définis dans la configuration de votre projet. Cela ne fait pas génère des bundles modernes et anciens pour une configuration module/nomodule, permettent d'installer et d'utiliser des packages npm contenant du code JavaScript moderne sans interrompre les anciens navigateurs.

webpack-plugin-modern-npm utilise cette technique pour compiler les dépendances npm ayant un champ "exports" dans leur package.json, car ils peuvent contenir une syntaxe moderne:

// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');

module.exports = {
  plugins: [
    // auto-transpile modern stuff found in node_modules
    new ModernNpmPlugin(),
  ],
};

Vous pouvez également implémenter la technique manuellement dans votre webpack configuration en recherchant un champ "exports" dans le package.json de modules au fur et à mesure qu'ils sont résolus. Si vous omettez la mise en cache pour des raisons de concision, une règle personnalisée l'implémentation peut se présenter comme suit:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      // Transpile for your own first-party code:
      {
        test: /\.js$/i,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      // Transpile modern dependencies:
      {
        test: /\.js$/i,
        include(file) {
          let dir = file.match(/^.*[/\\]node_modules[/\\](@.*?[/\\])?.*?[/\\]/);
          try {
            return dir && !!require(dir[0] + 'package.json').exports;
          } catch (e) {}
        },
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: false,
            configFile: false,
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
};

Lorsque vous utilisez cette approche, vous devez vous assurer que la syntaxe moderne est prise en charge par votre réducteur. Les deux Terser et uglify-es ont la possibilité de spécifier {ecma: 2017} afin de conserver et, dans certains cas, générer la syntaxe ES2017 lors de la compression et du formatage.

Vue d'ensemble

La propriété de consolidation permet de générer plusieurs ensembles de groupes dans le cadre et génère du code moderne par défaut. Par conséquent, le Rollup peut être configurés pour générer des bundles modernes et anciens avec les plug-ins officiels ; que vous utilisez probablement déjà.

@rollup/plugin-babel

Si vous utilisez la propriété de consolidation, la Méthode getBabelOutputPlugin() (fournies par l'API Rollup plug-in Babel officiel) transforme le code en bundles générés plutôt que de modules sources individuels. La propriété de consolidation permet de générer plusieurs ensembles de groupes dans le cadre une seule compilation, chacune avec ses propres plug-ins. Vous pouvez l'utiliser pour produire différents groupes pour les versions modernes et anciennes en les transmettant via un ensemble Configuration du plug-in de sortie Babel:

// rollup.config.js
import {getBabelOutputPlugin} from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: [
    // modern bundles:
    {
      format: 'es',
      plugins: [
        getBabelOutputPlugin({
          presets: [
            [
              '@babel/preset-env',
              {
                targets: {esmodules: true},
                bugfixes: true,
                loose: true,
              },
            ],
          ],
        }),
      ],
    },
    // legacy (ES5) bundles:
    {
      format: 'amd',
      entryFileNames: '[name].legacy.js',
      chunkFileNames: '[name]-[hash].legacy.js',
      plugins: [
        getBabelOutputPlugin({
          presets: ['@babel/preset-env'],
        }),
      ],
    },
  ],
};

Outils de compilation supplémentaires

Les propriétés de consolidation et de webpack sont hautement configurables, ce qui signifie généralement que chaque projet doit mettre à jour sa configuration pour activer la syntaxe JavaScript moderne dans les dépendances. Il existe également des outils de compilation de niveau supérieur qui privilégient la convention et les valeurs par défaut plutôt que telle que Parcel, Snowpack, Vite et WMR. La plupart de ces outils supposent que les dépendances npm peuvent contenir une syntaxe moderne et les convertit dans les niveaux de syntaxe appropriés lors de la compilation pour la production.

En plus des plug-ins dédiés pour webpack et Rollup, le code JavaScript moderne vous pouvez ajouter des groupes avec d'anciens fichiers de remplacement à n'importe quel projet à l'aide de devolution. La dévolution est un outil autonome qui transforme la sortie d'un système de compilation pour produire d'anciennes des variantes JavaScript, ce qui permet au regroupement et aux transformations de partir du principe que cible de sortie.