webpack 代码分离

789 阅读3分钟

| 什么是代码分离

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

常用的代码分离方法

  1. 配置入口起点entry
  2. 防止重复的分离方法
  3. 动态导入

image.png

| 修改入口起点

module.exports = {
-  // entry: './src/index.js'
+   entry: {
+       index: './src/index.js',
+       another: './src/another.js' // 其他入口文件
    },
    
    output: {
-      // filename: 'bundle.js',
+       filename: '[name].bundle.js', // [name]会匹配entry的键
    },
};

打包后结果

image.png

缺陷:
如果在两个文件中共享同一个库,比如lodash
那么两个文件都会把lodash压缩进去,这样就重复了
image.png

| 防止重复

module.exports = {
-  // entry: {
-  //     index: './src/index.js',
-  //     another: './src/another.js' // 其他入口文件
-  // },
+   entry: {
+       index: {
+           import: './src/index.js',
+           dependOn: 'shared', // 该文件共享shared指定模块
+       },
+       another: {
+           import: './src/another.js',
+           dependOn: 'shared', // 该文件共享shared指定模块
+       },
+       shared: 'lodash', // 共享lodash库
+   },
    output: {
       filename: '[name].bundle.js', // [name]会匹配entry的键
    },
};

执行npx webpack后 会生成三个文件,共享的模块会被单独抽离出来

image.png

可以看到有效减小了包大小 image.png

自动分割代码

module.exports = {
     entry: {
         index: './src/index.js',
         another: './src/another.js' // 其他入口文件
     },
    output: {
       filename: '[name].bundle.js', // [name]会匹配entry的键
    },
   optimization: {
        minimizer: [new CssMinimizerWebpackPlugin()],
+        splitChunks: {
+           chunks: 'all',
+      },
    },
};

执行结果

image.png

| 动态导入

动态导入有两种

  • webpack推荐的import()语法来实现动态导入
  • webpack特定的require.ensure

1.创建一个async-module.js文件

function getComponent() {
    // 动态加载
   return import('lodash').then(({ default: _ }) => {
        const ele = document.createElement('div');
        ele.innerHTML = _.join(['Hello', 'webpack'], ' ');
        return ele;
    });
}

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

2.index.js中引入

import './async-module.js'

3.执行打包后lodash同样被抽离出来了

image.png

如果需要与静态打包一起使用需要把代码自动分割打开

 optimization: {
        splitChunks: {
            chunks: 'all',
        },
    },

应用一 懒加载
懒加载也叫按需加载,当需要使用时才会加载,这样一来就提升了应用初始加载速度,减轻总体体积。

1.懒加载一个名为math的模块
/* webpackChunkName: 'math' */叫魔法注释,这里的作用是给math命名

const btn = document.createElement('button');
btn.textContent = '执行加法运算';
btn.addEventListener('click', () => {
    import(/* webpackChunkName: 'math' */ './math.js').then(({ add }) => {
        console.log(add(1, 2));
    });
});
document.body.appendChild(btn);

2.执行npx webpack
image.png

3.首次加载并没有加载math模块 image.png

当我们点击按钮时才会加载 image.png

应用二 预获取/预加载
webpack4.6+版本已经增加了预获取预加载的支持。
在声明import时使用以下内置指令可以告知浏览器执行:

  • prefetch:预获取
  • preload:预加载

预获取
1.在魔法注释中加入webpackPrefetch: true

const btn = document.createElement('button');
btn.textContent = '执行加法运算';
btn.addEventListener('click', () => {
    import(/* webpackChunkName: 'math',webpackPrefetch: true */ './math.js').then(({ add }) => {
        console.log(add(1, 2));
    });
});
document.body.appendChild(btn);

2.可以看到首次加载时已经被预获取出来了

image.png 在head标签里可以看到math被进行预获取处理了

image.png

预加载
1.在魔法注释中加入webpackPreload: true

const btn = document.createElement('button');
btn.textContent = '执行加法运算';
btn.addEventListener('click', () => {
    import(/* webpackChunkName: 'math',webpackPreload: true */ './math.js').then(({ add }) => {
        console.log(add(1, 2));
    });
});
document.body.appendChild(btn);

2.效果与懒加载一样

<link rel="preload" as="script" href="math.bundle.js">

不当地使用wepbackPreload会损害性能,所以使用的时候要小心

| 参考

# webpack优化之preload和prefetch