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",// 新增打包入口 lodashloadsh: "./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',// },},// ...}

