Webpack-optimalisaties - React-app die klaar is voor productie

Webpack is een statische modulebundel voor moderne JavaScript-toepassingen (bijv. React). Wanneer webpack onze applicatie verwerkt, bouwt het intern een afhankelijkheidsgrafiek die elke module in kaart brengt die ons project nodig heeft en genereert een of meer bundels.

De code/configuraties die in deze blog worden gebruikt, zijn beschikbaar in deze repo.

Een eenvoudige webpack.config.js voor React-toepassing ziet er als volgt uit.

const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js',
  },
  resolve: {
    modules: [path.join(__dirname, 'src'), 'node_modules'],
    alias: {
      react: path.join(__dirname, 'node_modules', 'react'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
        ],
      },
      { // If you are not using less ignore this rule
        test: /\.less$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
          },
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './index.html',
    }),
  ],
};

Out of the box, met deze bovenstaande configuratie, genereert het webpack één JS-bundelbestand. Voor grote projecten/applicaties wordt deze bundelgrootte erg groot (in MiB's). Het is dus essentieel om de enkele bundel in meerdere delen te splitsen en deze alleen te laden wanneer dat nodig is.

Hier komt lui laden in React om de hoek kijken. Het importeert de component in feite alleen wanneer dat nodig is. Het lui laden van componenten op routeniveau is een goed begin.

Wanneer we componenten lui laden, maakt webpack meerdere bundelbestanden op basis van onze routes, zonder dat er extra configuratie nodig is.

We kunnen hash-bestandsnamen gebruiken voor onze bundels, die alleen veranderen wanneer we onze app bouwen nadat we wijzigingen hebben aangebracht in dat specifieke stuk. Dus als er niets verandert, blijft dezelfde hash behouden en bedient de browser die bundelbestanden vanuit de cache. Raadpleeg dit voor andere hash-opties

output: {
  path: path.resolve(__dirname, 'build'),
  filename: '[name].[chunkhash].bundle.js',
}

Het is geweldig dat we onze bundels opsplitsen op basis van routes zonder enige extra configuratie in webpack, maar toch bevat onze hoofdbundel alle leverancierscodes (node_modules). We kunnen een paar configuraties toevoegen om webpack te vertellen hoe we de bundels verder willen spugen.

optimization: {
  splitChunks: {
    cacheGroups: {
      vendors: {
        test: /node_modules\/(?!antd\/).*/,
        name: "vendors",
        chunks: "all",
      },
      // This can be your own design library.
      antd: {
        test: /node_modules\/(antd\/).*/,
        name: "antd",
        chunks: "all",
      },
    },
  },
  runtimeChunk: {
    name: "manifest",
  },
}

Laten we de configuratie doornemen. optimization.splitChunks.cacheGroups is waar we onze chunks definiëren. Hier gebruikte ik de chunknaam vendors voor alle afhankelijkheden in node_modules behalve antd (Ant Design is een bibliotheek met UI-componenten) en ik gebruikte de chunknaam antd alleen voor de Ant-ontwerpafhankelijkheid.

De reden waarom we de leveranciers scheiden is dat, zodra ons project volwassen is geworden, we niet vaak nieuwe afhankelijkheden zullen toevoegen, dus onze chunk bestandsnaam hash zal niet veranderen voor elke build en de browser kan deze leverancier chunk bedienen uit de cache.
Ik heb andd gescheiden van het vendor chunk omdat dit onze eigen ontwerpbibliotheek kan zijn waar we regelmatig componenten toevoegen/bijwerken, dus elke verandering in dit chunk zou geen invloed moeten hebben op onze vendor chunk hash.
Ik heb ook het manifest eruit gehaald dat webpack bijhoudt, met informatie die nodig is om onze applicatie uit te voeren.

Als je de build-output hebt opgemerkt, is ons leveranciersgedeelte geel gemarkeerd en gemarkeerd als [groot]. Webpack is zo geconfigureerd dat het ons waarschuwt als de bundel groter is dan 244KiB. We kunnen deze waarschuwing gerust negeren, want hoe dan ook moeten onze bundels worden gegzipt en over het netwerk worden overgedragen. Deze gzip-codering wordt standaard uitgevoerd in sommige van de statische bestandsservers zoals netlify, serve en het is eenvoudig te configureren in andere AWS CloudFront
Hoe dan ook, als we willen gzip en webpack vertellen om gzipped-bestanden te gebruiken voor berekeningen, kunnen we de onderstaande configuratie toevoegen.

const CompressionPlugin = require('compression-webpack-plugin');

plugins: [
  new CompressionPlugin({
    test: /\.js(\?.*)?$/i,
  }),
],
performance: {
  hints: "warning",
  // Calculates sizes of gziped bundles.
  assetFilter: function (assetFilename) {
    return assetFilename.endsWith(".js.gz");
  },
}

Samenvattend,

  • We hebben een minimale webpack-configuratie ingesteld om onze react-app uit te voeren.
  • We gebruikten lazy loading om onze enkele bundel in meerdere delen te splitsen.
  • We hebben hash-bestandsnamen gebruikt om onze bundelbestanden te versies.
  • We spugen onze hoofdbundel verder om vendor- en antd-brokken te creëren.
  • We hebben gzip gebruikt om onze bundels te comprimeren (moet ook op onze statische hostingserver worden gedaan) om waarschuwingen voor de grootte van webpackbundels te vermijden.

U kunt de volledige webpack-configuratie hier bekijken

Dat was het, mensen, Bedankt voor het lezen van deze blog. Ik hoop dat het nuttig voor je is geweest. Geef commentaar op uw vragen en suggesties.

Referenties:

  • https://reactjs.org/docs/code-splitting.html
  • https://webpack.js.org/configuration/optimization/
  • https://webpack.js.org/plugins/split-chunks-plugin/
  • https://webpack.js.org/configuration/performance/