当一个项目包含多个入口时,比如多页面应用,将代码分离成更小的文件,使得我们更灵活得管理它们的加载,合理情况下减少加载的时间。
常用的分离方法有:配置入口起点、防止重复、动态加载、懒加载、预请求和预加载等。
一、入口起点
这是迄今为止最简单直观的分离代码的方式。比较直观,只需要如下配置:
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
};
使用键值对的形式配置多个入口,键是打包后的名,值则是源文件路径。这样做很方便的将多个入口的文件分开,但是也有隐患。例如index这个入口引入了某个大型库big.js,another也引入了big.js,打包的时候会重复打包big.js,生成两个文件,这显然是我们不希望看到的。
二、防止重复
防止重复的效果就是解决上面重复打包共同引入库。方法有两种:
一是配置入口依赖,配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块:
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js',
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
这个配置时表明入口都引用了shared这个依赖,然后指明shared是哪个依赖 这里以lodash为例,webpack在打包的时候就会将这个公共的抽离出来,只打包一次。
方法二:使用内置插件SplitChunksPlugin
SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
三、动态导入、懒加载
webpack支持动态的导入文件,情景是当代码运行到某一处时会导入需要的文件,而动态导入也会在打包时抽离这个导入的代码,例如下方这个函数执行的时候会导入lodash:
async function getComponent() {
const element = document.createElement('div');
const { default: _ } = await import('lodash');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
import执行是个Promise。
懒加载个人认为与动态导入方式差不多,先举个例子:
button.onclick = () => {
import('lodash')
}
当我点击按钮的时候会去加载lodash,如果我一直不点,就不会去加载,页面也不会发出请求。懒加载与动态加载的区别可能就在于它受主观操作影响。
预请求和预加载
Webpack v4.6.0+ 增加了对预获取和预加载的支持。
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 “resource hint(资源提示)”,来告知浏览器:
prefetch(预请求):将来某些导航下可能需要的资源
preload(预加载):当前导航下可能需要资源
prefetch的写法:
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
这个注释成为魔法注释,可以写一些指令。
preload的写法:
import(/* webpackPreload: true */ 'ChartingLibrary');
比较通俗的讲解这两个的作用和区别:
prefetch会在浏览器资源较为充足的时候去请求这个文件,相当于告知浏览器这个依赖我未来可能会用得到,但是不急,等有精力再帮我下载。上面的动态导入和懒加载可能会造成一种现象,就是在首屏加载时没有去加载这些资源会显得比较快,但是当点击按钮时,还得去等待请求一下依赖,交互起来可能会有体验感的影响,所以prefetch在空闲时提前请求这些资源,很大程度上能解决这个问题。
preload则是会在父chunk请求时并发请求,也就是告知浏览器,这个我马上要用到了,加急处理。在一些父chunk比较大时可以使用,比如父chunk很大,请求需要花费不少时间,这时候并发的去请求所需的依赖,而且又比较小时,父chunk下载完毕的时候,所需的依赖也下好了。使用preload需要注意分寸,不然会影响性能,很简单的道理,一个文件和它的依赖同时下载,那跟不分离代码有什么区别呢。