Réduire la taille de l'interface

Utiliser webpack pour réduire au maximum la taille de votre application

Lorsque vous optimisez une application, l'une des premières choses à faire est de réduire possible. Voici comment procéder avec webpack.

Utiliser le mode production (webpack 4 uniquement)

Webpack 4 a introduit le nouvel indicateur mode. Vous pourriez définir Utilisez cet indicateur sur 'development' ou 'production' pour indiquer le Webpack que vous créez. l'application pour un environnement spécifique:

// webpack.config.js
module.exports = {
  mode: 'production',
};

Veillez à activer le mode production lorsque vous compilez votre application pour la production. Webpack appliquera alors des optimisations telles que la minimisation et la suppression du code réservé au développement. dans les bibliothèques, entre autres.

Documentation complémentaire

Activer la minimisation

La minimisation consiste à compresser le code en supprimant les espaces superflus, en raccourcissant les noms de variables et ainsi de suite. Exemple :

// Original code
function map(array, iteratee) {
  let index = -1;
  const length = array == null ? 0 : array.length;
  const result = new Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

↓.

// Minified code
function map(n,r){let t=-1;for(const a=null==n?0:n.length,l=Array(a);++t<a;)l[t]=r(n[t],t,n);return l}

Webpack prend en charge deux méthodes pour réduire la taille du code: la minimisation au niveau du bundle et Options spécifiques au chargeur. Ils doivent être utilisés simultanément.

Minimiser au niveau du bundle

La minimisation au niveau du bundle compresse l'ensemble du bundle après la compilation. Voici comment cela fonctionne :

  1. Vous écrivez du code comme ceci:

    // comments.js
    import './comments.css';
    export function render(data, target) {
      console.log('Rendered!');
    }
    
  2. Webpack le compile approximativement comme suit:

    // bundle.js (part of)
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony export (immutable) */ __webpack_exports__["render"] = render;
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css__ = __webpack_require__(1);
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css_js___default =
    __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__comments_css__);
    
    function render(data, target) {
    console.log('Rendered!');
    }
    
  3. Un outil de réduction de taille le compresse approximativement dans ce qui suit:

    // minified bundle.js (part of)
    "use strict";function t(e,n){console.log("Rendered!")}
    Object.defineProperty(n,"__esModule",{value:!0}),n.render=t;var o=r(1);r.n(o)
    

Dans webpack 4, la minimisation au niveau du bundle est activée automatiquement, aussi bien en production et si vous n'en avez pas. Il utilise le réducteur UglifyJS. en arrière-plan. (Si vous devez désactiver la minimisation, utilisez simplement le mode Développement ou transmettez false à l'option optimization.minimize.)

Dans webpack 3,vous devez utiliser le plug-in UglifyJS. directement. Le plug-in est fourni avec webpack. Pour l'activer, ajoutez-le à plugins. de la configuration:

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

Options spécifiques au chargeur

La seconde méthode pour réduire la taille du code consiste à utiliser des options spécifiques au chargeur (ce qu'un chargeur l'est). Avec les options du chargeur, vous pouvez compresser les éléments l'outil de réduction ne peut pas réduire. Par exemple, lorsque vous importez un fichier CSS avec css-loader, le fichier est compilé dans une chaîne:

/* comments.css */
.comment {
  color: black;
}
// minified bundle.js (part of)
exports=module.exports=__webpack_require__(1)(),
exports.push([module.i,".comment {\r\n  color: black;\r\n}",""]);

L'outil de réduction ne peut pas compresser ce code, car il s'agit d'une chaîne. Pour réduire la taille du contenu du fichier, nous devons configurez le chargeur de cette manière:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { minimize: true } },
        ],
      },
    ],
  },
};

Documentation complémentaire

Spécifier NODE_ENV=production

Vous pouvez également réduire la taille de l'interface en définissant NODE_ENV variable d'environnement dans votre code à la valeur production.

Les bibliothèques lisent la variable NODE_ENV pour détecter le mode dans lequel elles doivent fonctionner (dans la de développement ou de production. Certaines bibliothèques se comportent différemment en fonction de cette variable. Pour Par exemple, lorsque NODE_ENV n'est pas défini sur production, Vue.js effectue des vérifications supplémentaires et imprime avertissements:

// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
  warn('props must be strings when using array syntax.');
}
// …

React fonctionne de la même manière. Elle charge un build de développement qui inclut les avertissements suivants:

// react/index.js
if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

// react/cjs/react.development.js
// …
warning$3(
    componentClass.getDefaultProps.isReactClassApproved,
    'getDefaultProps is only used on classic React.createClass ' +
    'definitions. Use a static property named `defaultProps` instead.'
);
// …

Ces vérifications et avertissements sont généralement inutiles en production, mais ils restent dans le code et augmenter la taille de la bibliothèque. Dans webpack 4,supprimez-les en ajoutant l'option optimization.nodeEnv: 'production':

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    nodeEnv: 'production',
    minimize: true,
  },
};

Dans webpack 3,utilisez plutôt DefinePlugin:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"'
    }),
    new webpack.optimize.UglifyJsPlugin()
  ]
};

L'option optimization.nodeEnv et DefinePlugin fonctionnent de la même manière : toutes les occurrences de process.env.NODE_ENV sont remplacées par la valeur spécifiée. Avec l'attribut ci-dessus:

  1. Webpack remplacera toutes les occurrences de process.env.NODE_ENV par "production":

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if (process.env.NODE_ENV !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    ↓.

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    
  2. Le réducteur supprime ensuite Branches if : comme "production" !== 'production' est toujours "false", et le plug-in comprend que le code à l'intérieur de ces branches ne s'exécutera jamais:

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    ↓.

    // vue/dist/vue.runtime.esm.js (without minification)
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    }
    

Documentation complémentaire

Utiliser des modules ES

La méthode suivante pour réduire la taille de l'interface est d'utiliser ES modules.

Lorsque vous utilisez des modules ES, webpack peut effectuer un tree shaking. On parle de "secousses d'arbre" lorsqu'un bundler parcourt l'ensemble de l'arborescence des dépendances, vérifie quelles sont les dépendances utilisées et supprime celles qui ne sont pas utilisées. Donc, Si vous utilisez la syntaxe du module ES, webpack peut éliminer le code inutilisé:

  1. Vous écrivez un fichier comportant plusieurs exportations, mais l'application n'en utilise qu'une:

    // comments.js
    export const render = () => { return 'Rendered!'; };
    export const commentRestEndpoint = '/rest/comments';
    
    // index.js
    import { render } from './comments.js';
    render();
    
  2. Webpack comprend que commentRestEndpoint n'est pas utilisé et ne génère pas de un point d'exportation distinct dans le bundle:

    // bundle.js (part that corresponds to comments.js)
    (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    const render = () => { return 'Rendered!'; };
    /* harmony export (immutable) */ __webpack_exports__["a"] = render;
    
    const commentRestEndpoint = '/rest/comments';
    /* unused harmony export commentRestEndpoint */
    })
    
  3. L'outil de réduction supprime la variable inutilisée:

    // bundle.js (part that corresponds to comments.js)
    (function(n,e){"use strict";var r=function(){return"Rendered!"};e.b=r})
    

Cela fonctionne même avec les bibliothèques si elles sont écrites avec des modules ES.

Toutefois, vous n'êtes pas obligé d'utiliser précisément le réducteur intégré de webpack (UglifyJsPlugin). Tout outil de réduction permettant la suppression des codes morts (par exemple, le plug-in Babel Minify). ou le plug-in Google Closure Compiler). fera l'affaire.

Documentation complémentaire

Optimiser les images

Les images représentent plus d'une de la moitié de la page. Bien qu'ils ne sont pas aussi essentiels que JavaScript (par exemple, ils ne bloquent pas l'affichage), ils consomment néanmoins la bande passante. Utilisez url-loader, svg-url-loader et image-webpack-loader pour les optimiser dans Webpack.

url-loader intègre de petits fichiers statiques dans l'application. Sans configuration, il utilise un fichier transmis, le place à côté du bundle compilé et renvoie une URL de ce fichier. Toutefois, si nous spécifions l'option limit, elle encodera les fichiers d'une taille inférieure à cette limite en tant qu'URL de données Base64 et renvoyez cette URL. Ce intègre l'image dans le code JavaScript et enregistre une requête HTTP:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        loader: 'url-loader',
        options: {
          // Inline files smaller than 10 kB (10240 bytes)
          limit: 10 * 1024,
        },
      },
    ],
  }
};
// index.js
import imageUrl from './image.png';
// → If image.png is smaller than 10 kB, `imageUrl` will include
// the encoded image: 'data:image/png;base64,iVBORw0KGg…'
// → If image.png is larger than 10 kB, the loader will create a new file,
// and `imageUrl` will include its url: `/2fcd56a1920be.png`

svg-url-loader fonctionne comme url-loader : sauf qu'il encode les fichiers avec l'URL au lieu du format Base64 1. Il est utile pour les images SVG, car les fichiers SVG ne sont que du texte brut, cet encodage est plus efficace en termes de taille.

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: "svg-url-loader",
        options: {
          limit: 10 * 1024,
          noquotes: true
        }
      }
    ]
  }
};

image-webpack-loader compresse les images à travers ce réseau. Il prend en charge les images JPG, PNG, GIF et SVG, et nous allons donc l'utiliser pour tous ces types d'images.

Ce chargeur n'intègre pas d'images dans l'application. Il doit donc fonctionner en association avec url-loader et svg-url-loader Pour éviter de copier et coller le code dans les deux règles (une pour les images JPG/PNG/GIF et une autre un pour les fichiers SVG), nous allons inclure ce chargeur en tant que règle distincte avec enforce: 'pre':

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        loader: 'image-webpack-loader',
        // This will apply the loader before the other ones
        enforce: 'pre'
      }
    ]
  }
};

Les paramètres par défaut du chargeur sont déjà prêts, mais vous pouvez les configurer si vous le souhaitez. consultez les options du plug-in. À choisissez les options à spécifier, consultez l'excellent guide sur les images optimisation.

Documentation complémentaire

Optimiser les dépendances

Plus de la moitié de la taille moyenne du JavaScript provient de dépendances, et une partie de cette taille peut être tout simplement inutile.

Par exemple, Lodash (à partir de la version 4.17.4) ajoute 72 Ko de code réduit au bundle. Mais si vous utilisez uniquement, 20 de ses méthodes, environ 65 Ko de code réduit ne font rien.

Autre exemple : Moment.js. Sa version 2.19.1 nécessite 223 Ko de code réduit, ce qui est énorme. la taille moyenne de JavaScript sur une page était de 452 Ko en octobre. 2017. Toutefois, 170 Ko de cette taille est la localisation fichiers. Si vous n'utilisez pas Moment.js avec plusieurs langues, ces fichiers gonflent le bundle sans l'objectif.

Toutes ces dépendances peuvent être facilement optimisées. Nous avons regroupé les approches d'optimisation dans un dépôt GitHub. Jetez-y un œil.

Activer la concaténation des modules ES (c'est-à-dire le hissage de champ d'application)

Lorsque vous créez un bundle, webpack encapsule chaque module dans une fonction:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

↓.

// bundle.js (part  of)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
  var __WEBPACK_IMPORTED_MODULE_0__comments_js__ = __webpack_require__(1);
  Object(__WEBPACK_IMPORTED_MODULE_0__comments_js__["a" /* render */])();
}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  __webpack_exports__["a"] = render;
  function render(data, target) {
    console.log('Rendered!');
  }
})

Auparavant, cette méthode était nécessaire pour isoler les modules CommonJS/AMD les uns des autres. Cependant, cela a ajouté un impact sur la taille et les performances de chaque module.

Webpack 2 prend en charge les modules ES qui, contrairement aux modules CommonJS et AMD, peuvent être regroupés sans encapsuler chacune d'elles avec une fonction. Grâce à Webpack 3, ce regroupement de produits est possible. concaténation des modules. Voici ce que fait la concaténation du module:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

↓.

// Unlike the previous snippet, this bundle has only one module
// which includes the code from both files

// bundle.js (part of; compiled with ModuleConcatenationPlugin)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

  // CONCATENATED MODULE: ./comments.js
    function render(data, target) {
    console.log('Rendered!');
  }

  // CONCATENATED MODULE: ./index.js
  render();
})

Vous voyez la différence ? Dans le bundle simple, le module 0 nécessite l'autorisation render du module 1. Avec concaténation du module : require est simplement remplacé par la fonction requise, et le module 1 est supprimés. Le bundle contient moins de modules et moins de frais généraux liés aux modules.

Pour activer ce comportement, activez l'option optimization.concatenateModules dans webpack 4:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    concatenateModules: true
  }
};

Dans webpack 3,utilisez ModuleConcatenationPlugin:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

Documentation complémentaire

Utilisez externals si vous avez à la fois du code webpack et autre que Webpack

Il se peut que vous ayez un projet volumineux dans lequel une partie du code est compilée avec webpack, et une partie du code ne l'est pas. J'aime un site d'hébergement vidéo, sur lequel le widget du lecteur peut être créé à l'aide de webpack, et la page environnante ; pourrait ne pas être:

<ph type="x-smartling-placeholder">
</ph> Capture d&#39;écran d&#39;un site d&#39;hébergement de vidéos
(un site d'hébergement vidéo entièrement choisi de façon aléatoire)
.

Si les deux éléments de code ont des dépendances communes, vous pouvez les partager pour éviter de télécharger le leur plusieurs fois. Pour ce faire, utilisez le externals du Webpack : elle remplace les modules par des variables ou ou d'autres importations externes.

Si les dépendances sont disponibles dans window

Si votre code autre que Webpack repose sur des dépendances disponibles en tant que variables dans window, un alias de dépendances en noms de variables:

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
};

Avec cette configuration, webpack ne regroupe pas les packages react et react-dom. À la place, ils seront remplacé par quelque chose comme ceci:

// bundle.js (part of)
(function(module, exports) {
  // A module that exports `window.React`. Without `externals`,
  // this module would include the whole React bundle
  module.exports = React;
}),
(function(module, exports) {
  // A module that exports `window.ReactDOM`. Without `externals`,
  // this module would include the whole ReactDOM bundle
  module.exports = ReactDOM;
})

Si les dépendances sont chargées en tant que packages AMD

Si le code autre que Webpack n'expose pas les dépendances dans window, les choses sont plus compliquées. Toutefois, vous pouvez toujours éviter de charger le même code deux fois si le code autre que Webpack les utilise dépendances en tant que packages AMD.

Pour ce faire, compilez le code webpack sous la forme d'un bundle AMD et de modules alias pour les URL de bibliothèque:

// webpack.config.js
module.exports = {
  output: {
    libraryTarget: 'amd'
  },
  externals: {
    'react': {
      amd: '/libraries/react.min.js'
    },
    'react-dom': {
      amd: '/libraries/react-dom.min.js'
    }
  }
};

Webpack encapsule le bundle dans define() et le fait dépendre des URL suivantes:

// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () { … });

Si le code autre que Webpack utilise les mêmes URL pour charger ses dépendances, ces fichiers seront chargés une seule fois. Les requêtes supplémentaires utilisent le cache du chargeur.

Documentation complémentaire

Récapitulatif

  • Activer le mode production si vous utilisez webpack 4
  • Minimisez votre code avec les options de réduction et de chargeur au niveau du bundle
  • Supprimez le code réservé au développement en remplaçant NODE_ENV par production.
  • Utiliser des modules ES pour activer le tree shaking
  • Compresser les images
  • Appliquer des optimisations spécifiques aux dépendances
  • Activer la concaténation des modules
  • Utilisez externals si c'est judicieux pour vous.