多入口

webpack 默认会从.index.js 文件入口开始打包,打包完成到 build 目录下的 bundle.js 文件下

这样有很多问题

  • bundle.js 包很大,不方便处理
  • 不利于首屏渲染,请求加载会浪费很多时间

所以可以进行多入口打包形成分包

1
2
3
4
5
6
7
8
9
10
entry: {
index: "./src/index.js",
main: "./src/main.js",
},
output: {
// 打包后的文件的输出目录
path: path.resolve(__dirname, "./build"),
//placeholder 占位符 name就是entry里的key
filename: "[name]-bundle.js",
},

这样打包以后就会有多个包

build/

1
2
3
index - bundle.js;
main - bundle.js;
index.html;

问题:如果多入口文件使用了同一个库,会造成每个入口文件都打包一遍(如 axios)

办法:shared

1
2
3
4
5
6
7
8
9
10
11
entry: {
index: { import: "./src/index.js", dependOn: "shared" },
main: { import: "./src/main.js", dependOn: "shared" },
shared: ["axios"],
},
output: {
// 打包后的文件的输出目录
path: path.resolve(__dirname, "./build"),
//
filename: "[name]-bundle.js",
},

这样打包后的 build

1
2
3
4
index - bundle.js;
main - bundle.js;
shared - bundle.js;
index.html;

动态导入

比如路由,点击到对应页面才加载对应的 js 文件,可以减少首屏的加载速度

因为不在 bundle.js 这个包里,是单独的打包出来的

1
2
3
4
const btn1 = document.createElement("button");
btn1.onclick = function () {
import("./router/about.js");
};

打包后 build/

1
src_router_about_js - bundle.js;

这个包当我点击对应按钮才会去请求,默认是不请求这个文件的

取值

默认 import()是可以拿到对应导入文件里的内容的

about.js

1
2
3
4
5
6
7
8
console.log(6);
const name = "sss";

export function about() {
console.log(about);
}

export default name;

index.js

1
2
3
4
5
6
7
8
const btn1 = document.createElement("button");
btn1.onclick = function () {
import("./router/about.js").then((res) => {
res.about();
// export default的值
res.default();
});
};

res 就相当于 about 这个模块

导出命名

1
2
3
4
output: {
// 可以对动态导出的包进行自定一命名
chunkFilename: "[id]-[name]-chunk.js",
},
1
src_router_about_js - src_router_about_js - chunk.js;

也可以通过魔法注释进行自定义

1
2
3
4
5
6
btn1.onclick = function () {
import(/* webpackChunkName: "about" */ "./router/about.js").then((res) => {
res.about();
res.default();
});
};

打包后

1
about - about - chunk.js;

SplitChunks

另外一种分包的模式是 splitChunk,它是使用 SplitChunksPlugin 来实现的:

因为该插件 webpack 已经默认安装和集成,所以我们并不需要单独安装和直接使用该插件;

只需要提供 SplitChunksPlugin 相关的配置信息即可;

Webpack 提供了 SplitChunksPlugin 默认的配置,我们也可以手动来修改它的配置:

比如默认配置中,chunks 仅仅针对于异步(async)请求,我们可以设置为 initial 或者 all;

initial为同步

all为全部

1
2
3
4
5
6
7
8
optimization: {
splitChunks: {
// async异步导入
// initial同步导入
// all 异步/同步导入
chunks: 'all',
}
}

SplitChunks 自定义配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
optimization: {
splitChunks: {
// async异步导入
// initial同步导入
// all 异步/同步导入
chunks: 'all',
// 最小尺寸: 如果拆分出来一个, 那么拆分出来的这个包的大小最小为minSize
minSize: 20000,
// 将大于maxSize的包, 拆分成不小于minSize的包
maxSize: 20000,
// minChunks表示引入的包, 至少被导入了几次
minChunks: 1,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
filename: '[id]_vendors.js',
// name: "vendor-chunks.js",
priority: -10,
},
// bar: {
// test: /bar_/,
// filename: "[id]_bar.js"
// }
default: {
minChunks: 2,
filename: 'common_[id].js',
priority: -20,
},
},
},
},

Chunks:

  • 默认值是 async

  • 另一个值是 initial,表示对通过的代码进行处理

  • all 表示对同步和异步代码都进行处理

minSize:

  • 拆分包的大小, 至少为 minSize;

  • 如果一个包拆分出来达不到 minSize,那么这个包就不会拆分;

maxSize:

  • 将大于 maxSize 的包,拆分为不小于 minSize 的包;

minChunks:

  • 至少被引入的次数,默认是 1;

  • 如果我们写一个 2,但是引入了一次,那么不会被单独拆分;

name:设置拆包的名称

  • 可以设置一个名称,也可以设置为 false;

  • 设置为 false 后,需要在 cacheGroups 中设置名称;

cacheGroups:

  • 用于对拆分的包就行分组,比如一个 lodash 在拆分之后,并不会立即打包,而是会等到有没有其他符合规则的包一起来打包;

    • test 属性:匹配符合规则的包;

    • name 属性:拆分包的 name 属性;

    • filename 属性:拆分包的名称,可以自己使用 placeholder 属性;

一般默认 Chunks 为 all,其他不做配置,使用默认即可

optimization.chunkIds 配置

optimization.chunkIds 配置用于告知 webpack 模块的 id 采用什么算法生成。

有三个比较常见的值:

  • natural:按照数字的顺序使用 id;

  • named:development 下的默认值,一个可读的名称的 id;

  • deterministic:确定性的,在不同的编译中不变的短数字 id

    • 在 webpack4 中是没有这个值的;

**最佳实践:
**

**开发过程中,我们推荐使用 named;
**

打包过程中,我们推荐使用 deterministic;

optimization. runtimeChunk 配置

配置 runtime 相关的代码是否抽取到一个单独的 chunk 中:

runtime 相关的代码指的是在运行环境中,对模块进行解析、加载、模块信息相关的代码;

比如 component、bar 两个通过 import 函数相关的代码加载,就是通过 runtime 代码完成的;

抽离出来后,有利于浏览器缓存的策略:

比如我们修改了业务代码(main),那么 runtime 和 component、bar 的 chunk 是不需要重新加载的;

比如我们修改了 component、bar 的代码,那么 main 中的代码是不需要重新加载的;

设置的值:

  • true/multiple:针对每个入口打包一个 runtime 文件,比如 main index 会打包两个;
  • single:不论几个入口都打包一个 runtime 文件;
  • 对象:name 属性决定 runtimeChunk 的名称,可以是字符串也可以是函数;

Prefetch 和 Preload

webpack v4.6.0+ 增加了对预获取和预加载的支持。

在声明 import 时,使用下面这些内置指令,来告知浏览器:

prefetch(预获取):将来某些导航下可能需要的资源

preload(预加载):当前导航下可能需要资源

与 prefetch 指令相比,preload 指令有许多不同之处:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。
  • **prefetch chunk 会在父 chunk 加载结束后始加载。
    **
  • **preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
    **
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻

prefetch在其他需要的资源下载完成以后,浏览器空闲时预先下载

preload 会在父 chunk 加载结束后开始加载

prefetch会在不使用时在控制台显示出来,preload 在不使用时不会显示