使用Webpack4优化Web性能(二)

使用Webpack4优化Web性能(二)_第1张图片

利用 Webpack 来优化 Web 性能属于_加载性能优化_的一部分: ☛ Web Performance Optimization with webpack

二、使用长期缓存

1、文件名输出

缓存包( bundle),并通过更改包名称(bundle name)来区分版本,将文件名替换成 [name].[chunkname].js

[hash] 替换:可以用于在文件名中包含一个构建相关(build-specific)的 hash;
[chunkhash] 替换:在文件名中包含一个 chunk 相关(chunk-specific)的哈希,比[hash]替换更好;
[contenthash] 替换:会根据资源的内容添加一个唯一的 hash,当资源内容不变时,[contenthash] 就不会变。

const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
-   entry: './index.js',
+   entry: {
+     main: './index.js',
+   },
    output: {
-     filename: 'bundle.js',
+     filename: '[name].[contenthash].js',  // / → bundle.8e0d62a03.js
      path: path.resolve(__dirname, 'dist')
    }
    plugins: [
      new HtmlWebpackPlugin({
-       title: 'Output Management'
+       title: 'Caching'
      })
    ],
  };

Hash vs chunkhash vs ContentHash

2、提取第三方库和样板代码

bundle 拆分成程序代码( app)、第三方库代码( vendor)和运行时代码( runtime)。
  • 开启智能 code splitting

在 webpack 4 中添加以下的代码,当第三方库代码大于 30 kb 时(未压缩和未gzip前),webpack 能够自动提取 vendor 代码,并且如果你在路由层面使用了代码分割的话,它也能够提取公共代码。

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
    }
  },
};

这样,每次打包都会生成两个文件:main.[chunkhash].jsvendors~main.[chunkhash].js (for webpack 4). 在 webpack 4 中, 当第三方库依赖很小的时候,vendor 包可能不会被生成,但也没关系。

  • webpack 运行时代码

Webpack 在入口 chunk 中,包含了其运行时的引导代码: runtime,以及伴随的 manifest 数据,runtime 是用来管理模块交互的一小片段代码。当你将代码分割成多个文件时,这段代码包含了 chunk id 和模块文件之间的映射,包括浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑。

Webpack 会将这个运行时包含到最后生成的 chunk 中,即 vendor。每次有任何块发生变化时,这段代码也会发生变化,导致 vendor bundle 发生变化。

【解决方法】:设置 runtimeChunktrue 来为所有 chunks 创建一个单一的运行时包:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    runtimeChunk: true,
  },
};
webpack 运行时代码很小,内联它,可以减少 HTTP 请求。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineSourcePlugin = require('html-webpack-inline-source-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      // Inline all files which names start with “runtime~” and end with “.js”.
      // That’s the default naming of runtime chunks
      inlineSource: 'runtime~.+\\.js',
    }),
    // This plugin enables the “inlineSource” option
    new InlineSourcePlugin(),
  ],
};

webpack-concepts-manifest

3、代码懒加载

单页应用中,使用 import对不关键的代码进行懒加载。
// videoPlayer.js
export function renderVideoPlayer() { … }

// comments.js
export function renderComments() { … }

// index.js
import {renderVideoPlayer} from './videoPlayer';
renderVideoPlayer();

// …Custom event listener
onShowCommentsClick(() => {
  import('./comments').then((comments) => {
    comments.renderComments();
  });
});

import() 表示你想要动态加载特定模块,当 webpack 看到 import('./module.js') 时,它会自动把该模块从 chunk 中移除,只有在执行的时候才会被下载。

这会使 main 模块更小,能够减少初始加载时间,并且也能很好的提高缓存,如果你在 main chunk 中改了代码,懒加载的模块不会被影响。

按路由/页面分割代码(Code Splitting),以避免加载不必要的内容。

单页应用中,除了通过 import() 进行懒加载,还可以通过框架层面的手段来进行。
React 应用懒加载——> Code Splitting(react-router) 或者 React.lazy(react doc)

WebpackGuides-Caching
WebpackConcepts-The Manifest

4、模块标识符

使模块标识符更稳定

在 webpack 构建时,每个 module.id 会基于默认的解析顺序(resolve order)进行增量,也就是说,当解析顺序发生变化,ID 也会随之改变。如:当新增一个模块的时候,它可能会出现在模块列表的中间,那么它之后的模块 ID 就会发生变化。

如果在业务代码里新引入一个模块,则:

  • main bundle 会随着自身的新增内容的修改,而发生变化 ——> 符合预期
  • vendor bundle 会随着自身的 module.id 的修改,而发生变化 ——> 【不符合预期】
  • runtime bundle 会因为当前包含一个新模块的引用,而发生变化 ——> 符合预期
const webpack = require('webpack');

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

为了解决这个问题,模块 ID 通过 HashedModuleIdsPlugin 来进行计算,它会把基于数字增量的 ID 替换成模块自身的 hash。这样的话,一个模块的 ID 只会在重命名或者移除的时候才会改变,新模块不会影响到它的 ID 变化。

[3IRH] ./index.js 29 kB {1} [built]
[DuR2] (webpack)/buildin/global.js 488 bytes {2} [built]
[JkW7] (webpack)/buildin/module.js 495 bytes {2} [built]
[LbCc] ./webPlayer.js 24 kB {1} [built]
[lebJ] ./comments.js 58 kB {0} [built]
[02Tr] ./ads.js 74 kB {1} [built]
    + 1 hidden module

你可能感兴趣的:(webpack4)