webpack 实践 8: 代码分割

| 分类 programming  | 标签 JS  javascript  webpack  webpack2  yarn  webpack&guides 

webpack 的代码分割功能能将代码分成多个 bundle,从而实现按需加载或并行加载。

有 3 种常用的代码分割方法:

  • 入口点:在配置文件中通过 entry 手动指定分割
  • 去重:使用 CommonsChunkPlugin 插件抽取重复模块
  • 动态加载:通过模块中的内联函数调用实现

入口点方法

这是最直观最简单的方法,缺点是需要手动配置分割,并且重复的模块可能会打包进多个 bundle 中,无法去重。

添加另一个模块 src/another-module.js

import _ from 'lodash';

console.log(
    _.join(['Another', 'module', 'loaded!'], ' ')
);

webpack.config.js:

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

module.exports = {
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Code Splitting'
    }),
    new CleanWebpackPlugin(['dist'])
  ]
};

src/index.js:

import _ from 'lodash';

function component() {
  var element = document.createElement('div');

  // Lodash, now imported by this script
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}

document.body.appendChild(component());

运行 yarn run build 会打包出两个文件 dist/index.bundle.jsdist/another.bundle.js。由于 src/index.jssrc/another-module.js 这两个源文件中都加载了 lodash,两个结果包中也都有包含了 lodash,没有去重。

去重

CommonsChunkPlugin 插件可以将通用的依赖模块抽取出来,保存到现有的 entry chunk 或一个新的 chunk 中。

先在 webpack.config.js 中使用该插件:

const webpack = require('webpack');

module.exports = {
  //...
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'common' // Specify the common bundle's name.
    })
  ]
};

运行 yarn run build 可以多出了一个打包文件: dist/common.bundle.js。即将原来两个包文件中重复的 lodash 模块抽取了出来并单独保存为了一个新的打包文件。

使用该插件可以将第三方库 (vendor) 代码抽取并打包为一个独立包。

可用于分割代码的其它插件和加载器:

动态加载

这里会有 2 种方法:

  • 使用 import() 语法(内部使用了 promises 实现,因此老浏览器上需使用 es6-promisepromise-polyfill
  • 另一个老方法,即使用 webpack 的 require.ensure

先在 webpack.config.js 中去除 CommonsChunkPlugin 插件,入口设置为一个,output 中添加 chunkFilename 配置:

//webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Code Splitting'
    }),
    new CleanWebpackPlugin(['dist'])
  ]
};

chunkFilename 配置项用来确定 non-entry chunk 文件的名字,详细见 output doc

src/index.js 修改成动态加载 lodash

function getComponent() {
    return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
        var element = document.createElement('div');
        element.innerHTML = _.join(['Hello', 'webpack'], ' ');
        return element;
    }).catch(error => 'An error occurred while loading the component');
}

getComponent().then(component => {
    document.body.appendChild(component);
});

注意 import 语句中的 webpackChunkName: "lodash" 注释,它将使动态加载的模块打包为 lodash.bundle.js。关于 webpackChunkName 和其它选项,见 import doc

打包后可看到生成了 dist/lodash.bundle.jsdist/index.bundle.js

Bundle 分析工具

  • 官方工具
  • webpack-chart: 以交互式的饼图显示 webpack stat
  • webpack-visualizer:可视化并分析你的 bundle,以查看模块都占用多少空间,是否有重复
  • webpack-bundle-analyzer: 即是一个插件,也是一个命令行工具,能以便捷的交互式的可绽放的 treemap 方式呈现 bundle 的内容。

参考


上一篇     下一篇