Publikuj, wysyłaj i instaluj nowoczesny kod JavaScript, aby przyspieszyć działanie aplikacji

Aby poprawić wydajność, włącz nowoczesne zależności i dane wyjściowe JavaScriptu.

Ponad 90% przeglądarek obsługuje nowoczesny JavaScript, ale powszechność użycia starszej wersji JavaScriptu pozostaje sporym źródłem problemów z wydajnością. w sieci.

Nowoczesny JavaScript

Współczesny kod JavaScript nie jest opisany jako kod napisany w konkretnym ECMAScript wersji specyfikacji, ale raczej w składni obsługiwanej przez wszystkie przeglądarki. Nowoczesne przeglądarki, takie jak Chrome, Edge, Firefox i Safari, ponad 90% rynku przeglądarek oraz różne przeglądarki korzystające z tych samych bazowych mechanizmów renderowania składają się dodatkowe 5%. Oznacza to, że 95% globalnego ruchu internetowego pochodzi z przeglądarek. które obsługują najczęściej używane funkcje języka JavaScript w ostatnich 10 latach lat, w tym:

  • Klasy (ES2015)
  • Funkcje strzałek (ES2015)
  • Agregaty (ES2015)
  • Określanie zakresu bloku (ES2015)
  • Niszczenie (ES2015)
  • Parametry odpoczynku i rozprzestrzeniania się (ES2015)
  • Skrót do obiektu (ES2015)
  • Asynchroniczne/oczekujące (ES2017)

Funkcje w nowszych wersjach specyfikacji języka zazwyczaj mają gorsze wyniki spójną obsługę w nowoczesnych przeglądarkach. Na przykład wiele wersji ES2020 i ES2021 są obsługiwane tylko na 70% rynku przeglądarek – nadal większość przeglądarek, ale nie na tyle, by można było bezpiecznie korzystać z tych funkcji. Ten co oznacza, że chociaż „nowoczesny”, JavaScript jest ruchomym celem. W wersji ES2017 najszerszy zakres zgodności z przeglądarkami uwzględniając większość popularnych nowoczesnych funkcji składni. Innymi słowy, wersja ES2017 jest najbliższa nowoczesnej składni.

Starsza wersja JavaScriptu

Starsza wersja JavaScript to kod, który w szczególności nie używa powyższych języków funkcje zabezpieczeń. Większość programistów tworzy kod źródłowy przy użyciu nowoczesnej składni, ale kompilować wszystko do starszej składni, aby zapewnić lepszą obsługę przeglądarek. Kompiluję do starszej składni zwiększa obsługę przeglądarek, ale efekt często jest taki, niż nam się wydaje. W wielu przypadkach wsparcie wzrasta z około 95% do 98%, a jednocześnie ponosić znaczące koszty:

  • Starsza wersja JavaScript jest zwykle o około 20% większa i wolniejsza niż odpowiadający współczesnym kodom. często występują błędy w narzędziach i błędna konfiguracja; jeszcze bardziej poszerzają tę lukę.

  • Zainstalowane biblioteki stanowią nawet 90% typowej środowiska produkcyjnego kod JavaScript. W kodzie bibliotecznym wykorzystywany jest jeszcze starszy kod JavaScript z powodu polyfill i duplikacji danych pomocniczych, których można by uniknąć publikując nowoczesny kod.

Nowoczesny JavaScript w npm

W środowisku Node.js ustandaryzowane jest ostatnio pole "exports", które pozwala zdefiniować punkty wejścia przesyłki:

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

Moduły, do których odwołuje się pole "exports", sugerują wersję węzła o co najmniej 12.8, która obsługuje standard ES2019. Oznacza to, że każdy moduł, do którego odwołuje się funkcja Pole "exports" można pisać w nowoczesnym języku JavaScript. Konsumenci pakietu muszą zakładaj, że moduły z polem "exports" zawierają nowoczesny kod i transpiluj je, jeśli niezbędną.

Tylko nowoczesne

Jeśli chcesz opublikować pakiet z nowoczesnym kodem i pozostawić go aby konsument przetranspilować go, gdy używa go jako zależności – używaj tylko parametru "exports".

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

Nowoczesna ze starszą wersją zastępczą

Aby opublikować pakiet, użyj pola "exports" razem z polem "main" za pomocą nowoczesnego kodu, ale także zasobów zastępczych ES5 i CommonJS dla starszych przeglądarki.

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

Nowoczesność ze starszą wersją kreacji zastępczych i optymalizacją pakietów ESM

Oprócz zdefiniowania zastępczego punktu wejścia CommonJS pole "module" może został użyty do wskazania podobnego starszego pakietu kreacji zastępczych, ale takiego, który korzysta Składnia modułów JavaScript (import i export).

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

Wiele firm tworzących pakiety, takie jak webpack i Rollup, korzysta z tego pola, aby uzyskiwać funkcji modułu oraz włączyć trząsa się drzewo. To nadal starszy pakiet, który nie zawiera żadnego nowoczesnego kodu oprócz składni import/export, więc użyj tej metody, aby wysyłać nowoczesny kod za pomocą starszego typu kreacji zastępczej, która jest nadal zoptymalizowana pod kątem grupowania.

Nowoczesny JavaScript w aplikacjach

Zależności od innych firm stanowią zdecydowaną większość typowej produkcji kodu JavaScript w aplikacjach internetowych. Chociaż w przeszłości zależności npm miały została opublikowana jako starsza składnia ES5, nie jest to już bezpieczne założenie. może spowodować, że aktualizacje zależności zakłócają obsługę przeglądarki w aplikacji.

Wraz ze wzrostem liczby pakietów npm przenoszonych do nowoczesnego JavaScriptu aby upewnić się, że narzędzia do tworzenia są skonfigurowane pod ich kątem. Jest duże prawdopodobieństwo, że niektóre pakiety npm, na których Ci zależy, korzystają już z nowoczesnych funkcje językowe. Dostępnych jest wiele sposobów korzystania z nowoczesnego kodu z npm bez uszkodzenia aplikacji w starszych przeglądarkach, ale ogólne koncepcja to transpilacja zależności systemu kompilacji na tę samą składnię. jako kod źródłowy.

Webpack

Od wersji Webpacka 5 można skonfigurować typ składni pakietu internetowego, którego będzie używać. podczas generowania kodu pakietów i modułów. Nie spowoduje to transpilacji lub zależności, ma to wpływ tylko na „klej” wygenerowany przez Webpack. Aby określić cel obsługi przeglądarki, dodaj konfiguracja listy przeglądarek, w swoim projekcie lub bezpośrednio w konfiguracji pakietu internetowego:

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

Można również skonfigurować pakiet internetowy w celu wygenerowania zoptymalizowanych pakietów, pomijanie zbędnych funkcji kodu przy kierowaniu na nowoczesne moduły ES. dla środowiska. Spowoduje to również skonfigurowanie pakietu internetowego do wczytywania pakietów podzielonych kodu za pomocą <script type="module">

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

Dostępnych jest wiele wtyczek pakietów internetowych, które umożliwiają kompilować i wysyłać nowoczesny JavaScript, nie rezygnując z obsługi starszych przeglądarek, takich jak Optimize Plugin i BabelEsmPlugin.

Wtyczka Optimize

Wtyczka Optimize to pakiet internetowy wtyczka przekształcająca końcowy kod w pakiecie z nowoczesnego do starszego JavaScriptu a nie poszczególnych plików źródłowych. To autonomiczna konfiguracja, która umożliwia w konfiguracji Webpacka, zakładając, że wszystko jest w języku nowoczesnym JavaScript specjalne rozgałęzienia na wiele danych wyjściowych lub składni.

Wtyczka Optimize działa na pakietach, a nie na pojedynczych modułach, przetwarza kod aplikacji i zależności w równym stopniu. Dzięki temu można bezpiecznie korzystać z nowoczesnych zależności JavaScript z npm, ponieważ ich kod zostanie grupowane i transpilowane do prawidłowej składni. Może też być szybsza niż rozwiązań, które składają się z 2 etapów kompilacji, a jednocześnie generują osobne pakiety dla nowoczesnych i starszych przeglądarek. Te 2 pakiety to: przeznaczone do ładowania wzorzec modułu/nomodule.

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

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

Usługa Optimize Plugin może być szybsza i wydajniejsza niż niestandardowy pakiet internetowy które zwykle łączą w skład kodu zarówno nowoczesny, jak i starszy kod. it obsługuje też uruchamianie Babel i minimalizuje pakiety w usłudze Terser z osobnymi optymalnymi ustawieniami w przypadku starszych i nowoczesnych danych wyjściowych. Na koniec uzupełnione są pola polyfill wymagane przez wygenerowany kod starsze pakiety są wyodrębniane do dedykowanego skryptu, dzięki czemu nigdy duplikowane lub niepotrzebnie ładowane w nowszych przeglądarkach.

Porównanie: transpilacja modułów źródłowych dwukrotnie i wygenerowanych pakietów.

BabelEsmPlugin

BabelEsmPlugin to pakiet internetowy. , która współpracuje z @babel/preset-env aby generować nowoczesne wersje istniejących pakietów i wysyłać do nich mniej transpilowanego kodu nowoczesnych przeglądarek. To najpopularniejsze, ogólnodostępne rozwiązanie dla module/nomodule, który jest używany przez biblioteki Next.js i Interfejs wiersza poleceń 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 obsługuje szeroką gamę konfiguracji pakietów internetowych, ponieważ uruchamia dwie zasadniczo oddzielne kompilacje aplikacji. Dwukrotne kompilowanie może zająć w przypadku dużych aplikacji wymaga nieco więcej czasu, ale ta metoda BabelEsmPlugin, aby płynnie zintegrować go z istniejącymi konfiguracjami pakietów internetowych i jest jedną z najwygodniejszych dostępnych opcji.

Skonfiguruj moduł babel-loader, aby transpilować moduły node_modules

Jeśli używasz babel-loader bez jednej z 2 poprzednich wtyczek, musisz wykonać ważny krok, aby korzystać z nowoczesnego kodu npm JavaScript modułów. Zdefiniowanie 2 oddzielnych konfiguracji babel-loader umożliwia do automatycznego kompilowania nowoczesnych funkcji językowych dostępnych w języku node_modules na ES2017, jednocześnie transpilując własny kod za pomocą Babel wtyczki i gotowe ustawienia określone w konfiguracji projektu. Nie może generować nowoczesne i starsze pakiety na potrzeby konfiguracji modułów/brak modułów, umożliwia instalowanie i używanie pakietów npm zawierających nowoczesny JavaScript bez naruszania starszych przeglądarek.

webpack-plugin-modern-npm korzysta z tej metody do kompilowania zależności npm z polem "exports" w package.json, bo mogą one zawierać nowoczesną składnię:

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

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

Możesz też wdrożyć technikę ręcznie w pakiecie internetowym. przez sprawdzenie pola "exports" w package.json funkcji po ich rozwiązaniu. Pomijanie buforowania dla zwięzłości, niestandardowy implementacja może wyglądać tak:

// 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'],
          },
        },
      },
    ],
  },
};

Stosując tę metodę, musisz zadbać o nowoczesną składnię, która jest obsługiwana przez i minifikator. Terser i uglify-es pozwala na określenie {ecma: 2017}, aby zachować, a w niektórych przypadkach generowania składni ES2017 podczas kompresji i formatowania.

Podsumowanie

Usługa o pełnym zakresie ma wbudowaną obsługę generowania wielu zestawów pakietów w ramach w ramach pojedynczej kompilacji i domyślnie generuje nowoczesny kod. Dzięki temu podsumowanie być skonfigurowane pod kątem generowania nowoczesnych i starszych pakietów za pomocą oficjalnych wtyczek z której często korzystasz.

@rollup/plugin-babel

Jeśli korzystasz z usługi o pełnym zakresie, Metoda getBabelOutputPlugin() (dostarczone przez usługę Rollup oficjalna wtyczka Babel) przekształca kod w wygenerowanych pakietach, a nie w poszczególnych modułach źródłowych. Usługa o pełnym zakresie ma wbudowaną obsługę generowania wielu zestawów pakietów w ramach w ramach jednej kompilacji, z których każda ma własne wtyczki. Za ich pomocą możesz tworzyć Pakiety dla współczesnych i starszych, przechodząc przez różne Konfiguracja wtyczki wyjściowej 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'],
        }),
      ],
    },
  ],
};

Dodatkowe narzędzia do kompilacji

Usługę Rollup i Webpack można łatwo konfigurować, co oznacza, że każdy projekt musi zaktualizować swoją konfigurację i włączyć nowoczesną składnię JavaScript w zależnościach. Istnieją też wyższe narzędzia do kompilacji, które preferują konwencję i ustawienia domyślne. Parcel, Snowpack, Vite i WMR. Większość z tych narzędzi przyjmij, że zależności npm mogą zawierać nowoczesną składnię i przetranspilują je do odpowiednie poziomy składni podczas tworzenia środowiska produkcyjnego.

Oprócz specjalnych wtyczek do pakietów internetowych i Rollup, nowoczesne JavaScript Pakiety ze starszymi kreacjami zastępczymi można dodać do dowolnego projektu za pomocą rozwoju. Rozwój niezależne narzędzie, które przekształca dane wyjściowe z systemu kompilacji w celu utworzenia wariantów JavaScriptu, co pozwala łączyć w pakiety i przekształcać w ten sposób miejsca docelowego wyjściowego.