Webpack
中文:
代码分离 | webpack 中文文档
我们在使用像 Vue 这样的框架进行开发的时候会发现Vue
所有的配置文件都是放在build
文件夹下的,build
文件夹主要存放的是编译的配置文件,所以我们也可以做出更改:
--projectName
----build
-----webconfig.common.js
-----webconfig.dev.js
-----webconfig.prod.js
但是现在当运行 npm run build
的时候会发现dist
文件夹被打包到了build
的目录下,这是因为我们把配置文件放到了build
的文件夹下却没有对配置做出变更,而配置文件依然执行的是当前目录下(也就是和build
平级的目录)。
// ...
module.exports = {
output: {
filename: "main.js",
// 输出到上级目录
path: path.resolve(__dirname, "../dist"),
},
// ...
}
场景
lodash
是一个功能集合的工具包,它提供了很多方法方便开发者高效的进行开发,我们在这里也是用一下这个代码库。
安装:
$ npm install loadsh --save
在index.js
中进行使用:
import _ from "lodash";
console.log(_.join(["a", "b", "c"], "--"));
join
方法接收了三个字符串的数组且用 — 进行拼接,这个时候进行npm run build
进行项目打包的时候就会发现Webpack
把lodash
和业务代码都被打包到了main.js
文件中。
假设我们的业务代码非常的庞大,比如:
import _ from "lodash";
console.log(_.join(["a", "b", "c"], "--"));
// 此处假设有 100 行业务代码
这个再进行打包肯定还会把loadsh
库和业务逻辑统一打包到main.js
文件中,但是这样存在一个潜在的问题就是文件非常的庞大,每次更改业务逻辑代码后都需要重新进行打包,用户就需要重新加载这庞大的mian.js
文件,相应的加载时间就会很长,另外loadsh
库我们基本是不会进行更改的,所以我们不希望每次更改src/index.js
文件loadsh
也重新进行打包。
手动进行文件分割
新建loadsh.js
文件,把loadsh
挂在到window
对象上:
import _ from "lodash";
window._ = _;
index.js
只负责我们的业务逻辑:
console.log(_.join(["a", "b", "c"], "--"));
// 此处假设有 1000 行业务代码
在配置文件中新增一个打包入口loadsh
:
// ...
module.exports = {
entry: {
main: "./src/index.js",
// 新增打包入口 lodash
loadsh: "./src/loadsh.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "../dist"),
},
// ...
}
新增一个build:test
脚本命令,配置一个开发环境的打包(因为dev
命令打包生产的dist
文件夹是在电脑内存当中我们看不到):
{
// ...
"scripts": {
"dev": "webpack serve --config ./build/webpack.dev.js",
"build:test": "webpack --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
}
// ...
}
此时我们再进行打包就会看到index.js
和loadsh.js
就分别被打包了出来了:
projectName
├─babel.config.js
├─package-lock.json
├─package.json
├─postcss.config.js
├─tree.txt
├─src
| ├─index.html
| ├─index.js
| └loadsh.js
├─dist
| ├─index.html
| ├─loadsh_2d3d6f255a1e834f9349.js
| └main_2d3d6f255a1e834f9349.js
├─build
| ├─webpack.common.js
| ├─webpack.dev.js
| └webpack.prod.js
这个时候用户访问页面的时候浏览器就会加载两个js文件,且两个JS
文件会同时加载,这样会比加载一个JS
文件快(非绝对性),当我们修改src/index.js
文件的业务代码后,用户就只加载新的main.js
文件既可。
splitchunksplugin 配置
Webpack
中文:
代码分离 | webpack 中文文档
虽然代码被分开打包了但是这时还有个问题,就是每次index.js
文件被打包后浏览器都需要重新加载两个文件(index.js
和loadsh.js
),这很显示是没有必要的。
那什么是Code Splitting
?它是一种用于代码分割的工具。
其实在Webpack
之前,我们可以手动的进行代码分割这样也能提高项目的性能,所以Code Splitting
和Webpack
没有本质的关系,而现在webpack4
中已经绑定了Code Splitting
的插件 SplitChunksPlugin
。
新增optimization.splitChunks
配置项:
module.exports = {
// ...
// optimization表示优化
optimization: {
usedExports: true,
splitChunks: {
// 默认配置为 chunks: 'acync',只分割异步加载的代码
chunks: 'all',
},
},
// ...
}
这样webpack
就自动帮我们进行了代码分割。
可以看到打包后dist
文件夹有三个文件,main.js
文件是src/index.js
文件的业务逻辑,vendors~main.js
文件是对lodash
库的分割打包。
例如我们再新增一个src/math.js
文件进行引入:
import _ from "lodash";
import math from "./math.js";
const res = _.join([1, 2, 3, 4, 5, 6]);
console.log(res);
接着再进行打包,我们发现dist
文件夹下并没有多出math.js
文件,而是把math.js
的逻辑打包到了main.js
文件中:
:::info
可见Webpack
并不是每引入一个文件就会打包产出一个单独的文件,它由一个默认配置来决定的,也就是根据optimization.splitChunks
来决定如何进行分割。
:::
异步引入
以上的配置是基于同步代码引入实现的代码分割,如何实现异步的代码分割呢?
假如我们在index.js
文件异步引入lodash
包:
function getComponent() {
// 之所以需要 default,是因为 Webpack4 在导入 CommonJS 模块时,将不再解析为 module.exports 的值
// 而是为 CommonJS 模块创建一个 artificial namespace 对象
return import("lodash").then(({ default: _ }) => {
console.log(_.join(["a", "b", "c"], "--"));
})
}
getComponent();
此时运行npm run build:test
打包就会看到异步加载的lodash
代码被分割了出来。
其中0.js
就是对lodash
文件异步加载的分割产出,main.js
文件是src/index.js
业务逻辑的产出,vendors~main.js
是对lodash
同步加载的产出(个人认为,此说法不一定准确)。
其实做异步代码分割的时候是不需要配置splitChunks
的,这是因为optimization.splitChunks.chunls
的默认值就是async
,表示异步加载,例如我们删除optimization.splitChunks.chunls
的配置:
module.exports = {
// ...
// optimization表示优化
optimization: {
usedExports: true,
// 删除 splitChunks
// splitChunks: {
// 默认配置为 chunks: 'acync',只分割异步加载的代码
// chunks: 'all',
// },
},
// ...
}