什么是代码分离?好处?怎么分?

代码分离是 webpack 中最引人注目的特性之一,能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载。代码分离可以获取更小的budle,以控制资源加载优先级,使用合理会极大影响加载时间。

常用的方法有三种:

  1. 入口起点:使用 entry 配置手动的分离代码
  2. 防止重复:使用 Enty dependencies 或者 SplitChunksPlugin 去重和分离 chunk
  3. 动态导入:通过模块的内联函数调用来分离代码

    入口起点

    迄今为止最为简单直观的方法

    准备

    在src中新建 another.js ```javascript import _ from ‘lodash’ // 引入 lodash

console.log(_.join([‘another’, ‘hello’, ‘lodash’], ‘ ‘)) // 拼接字符串

  1. <a name="PXqN1"></a>
  2. #### 配置
  3. ```javascript
  4. entry: {
  5. index: './src/index.js', // 配置 index.js 打包入口
  6. another: './src/another.js' // 配置 another.js 打包入口
  7. },
  8. output: { // 出口文件
  9. filename: '[name].bundle.js', // [name] 继承入口文件名 + .bundle.js 后缀
  10. path: path.resolve(__dirname, './dist'), // 出口文件路径
  11. clean: true // 生成新文件的同时是否删除旧文件
  12. },

npx webpack 打包,发现 another.bundle.js 比 index.bundle.js 大很多,原因是我们在 another.js 中引入了 lodash ,webpack会把文件所需要的包也打包进去。
image.png

注意

如果多个文件需要引入了同样的包,webpack依然会全部编译,这就导致每个包都很大,严重影响打包效率,这也是 入口起点 代码分离方式的痛点。

防止重复

这个方法依然需要在 entry 中做一些配置,但是相比上面那种方法,他能有效地避免打包重复的代码

准备

我们在 index.js 中也引入 lodash

import _ from 'lodash' // 引入 lodash

console.log(_.join(['index', 'hello', 'lodash'], ' ')) // 拼接字符串

配置

entry: {
  index: {
      import: './src/index.js',
      dependOn: 'shared'
  },
  another: {
      import: './src/another.js',
      dependOn: 'shared'
  },
  shared: 'lodash'
}, // 入口文件路径
output: { // 出口文件
  filename: '[name].bundle.js', // 出口文件名
  path: path.resolve(__dirname, './dist'), // 出口文件路径
  clean: true // 生成新文件的同时是否删除旧文件
},

npx webpack 打包,多出来了一个 shared.bundle.js 文件,他就把我们的lodash单独打包出来了
image.png

SplitChunksPlugin

配置
entry: {
    index: './src/index.js', // 配置 index.js 打包入口
    another: './src/another.js' // 配置 another.js 打包入口
},
optimization: {
    splitChunks: {
        chunks: 'all' // 配置 SplitChunksPlugin 自动抽离代码
    }
}

直接运行 npx webpack 就能达到上述效果

动态导入

webpack提供了两个动态代码拆分,第一种也是推荐的方式是,使用符合 ECMAScript 提案的 import() 语法来实现动态导入。第二种是webpack特定的 require.ensure。

准备

在开始之前,我们先将上述示例配置的多余 entry 和 optimization 全部注释掉。
我们新建一个 async_module.js 并写入以下内容

function getComponent() {
    const element = document.createElement('div');
    return import('lodash')
        .then(({ default: _ }) => {
            const 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);
});

在index.js中引入 async_module.js 并打包,发现 lodash 已经被单独打包出来了
image.png

优化

我们能否同时打包动态和静态资源呢?
我们将之前的注释打开,然后通过 npx webpack 打包,我们发现动态和静态引入可以被打包出一个公共资源包

疑问

看起来我们通过 SplitChunksPlugin 和 动态导入 没有明显区别,甚至动态导入还会让代码变得复杂,那我们为什么不采用静态导入,而要使用动态导入呢?