8 代码分割
- 代码分割:按需求将代码分割为不同的文件,使项目更快打开。
- 经常使用代码打包工具(如webpack)处理代码分割
8.1 为什么进行代码分割
8.1.1 为何进行单入口处理
- 例如vue单页面项目等单入口(一个入口文件打包后对应一个出口js文件)情况,意味所有代码在一个文件里(一个app.js里包含所有的引入的其他js文件),这样会导致代码过大。
一个有200多个页面的项目,全都在一个js文件里,意味着要展示首页,要将所有的js文件下载下来。
文件过多,首页加载速度变慢
- 需要把不是马上用到的代码拆分出来,用于加快首屏速度
例如d.js不是首页要用到的js,可能是进行某个操作要用到的js,那么就将d.js从app.js拆分出来,单独打包。这样就会打出2个js文件,一个app.js,一个d.js. —— 异步引入
8.1.2 为何进行多入口处理
单入口和多入口要处理的问题是不同的
- 单入口处理的问题是所有的文件都在一个文件里,为避免体积过大,因此要拆为异步引入。
- 但多入口本身已经拆分为了不同的入口不同的页面,每个入口对应一个结果,不会存在体积过大的问题。
多入口的问题
- 问题描述:重复加载同一段逻辑代码
- 问题举例
- app.js和app2.js同时依赖b.js,打包后的两个js分别打包了b.js文件。
- b.js分别在app.js和app2.js都存在。
- 浏览器运行app.js时 ,将b.js代码下载一遍,运行app2.js时,又将b.js下载一遍。
- 解决思路:相同的模块独立打包。
- 浏览器存在缓存,对于加载过的文件会缓存下来。
- 因此加载app.js时加载b.js,并缓存,再加载app2.js时直接使用缓存的b.js而无需再次加载
8.2 何时代码分割
8.2.1 何时处理单入口情况
异步引入的前提分析
- 不是说所有首页不会马上用到的js都可以拆为异步引入
- 拆成异步引入 → 减少打包的app.js的体积 → 加快首屏速度
- 拆成异步引入 → 但增加http请求的数量 → 增加服务器压力
- 与base64本质相似
- base64可以减少http请求,但是不能滥用
- 大的图片转为base64 → 图片体积加到app.js → 增加打包的app.js → 减慢首屏加载速度
- 原因:app.js下载完成后才能加载页面 → app.js体积过大 → 下载速度会变慢
异步引入的结论
- 除非文件非常大&首页不会马上使用 → 推荐异步引入
8.3 如何进行代码分割
8.3.1 如何处理单入口情况
- 异步引入的方法
- import
- 较多使用
- require.ensure
- import
1 情景构建&异步引入
例如app.js需要在3s后使用a.js。
// app.js
setTimeout(() => {
import('./a.js').then(res => {
// res是a.js export的内容
console.log("🚀 ~ import ~ res:", res);
console.log("🚀 ~ import ~ res.default:", res.default);
});
}, 3000);
// a.js
let b = 3;
console.log(b);
export default b + 1;
2 打包
打包结果:除了app1.xxxx.xxxx.js,还有一个js文件。
3 实际效果
浏览器打开index.html,打开控制台。
可以看到,初始只有3个文件。
3s后请求第4个文件。
控制台打印结果
4 指定文件命名
添加魔法注释magic comment——/*webpackChunkName:"a"*/
// app.js
simport "./test.css";
setTimeout(() => {
import(/*webpackChunkName:"a"*/'./a.js').then(res => {
// res是a.js export的内容
console.log("🚀 ~ import ~ res:", res);
console.log("🚀 ~ import ~ res.default:", res.default);
});
}, 3000);
打包结果
5 require.ensure的写法
以上是import的写法,本节介绍require.ensure的写法。
- 第1个参数是第2个参数回调要用到的依赖
- 文件命名在于第3个参数
// app.js
import "./test.css";
setTimeout(() => {
// import(/*webpackChunkName:"a"*/'./a.js').then(res => {
// // res是a.js export的内容
// console.log("🚀 ~ import ~ res:", res);
// console.log("🚀 ~ import ~ res.default:", res.default);
// });
// 第1个参数是第2个参数回调要用到的依赖
require.ensure(["jquery"], ($) => {
let b = require("./a.js");
// 此处的b与import写法的res是一样的
console.log("🚀 ~ require.ensure ~ b:", b);
console.log("🚀 ~ require.ensure ~ b.default:", b.default);
}, "a");// 文件命名在于第3个参数
}, 3000);
8.3.2 如何处理多入口情况
1 情景构建
app.js和app2.js都引入了a.js
// app.js
import "./test.css";
import './a.js'
// app2.js
import './a.js'
console.log("app2.js")
a.js不赘述了,跟之前一样。
直接打包,看无处理的情况是怎样的。
- app.js和app2.js打包的文件均有a.js的代码。
- 即a.js的代码要加载两次
2 配置
在optimization配置splitChunks属性。
const htmlwebpack = require('html-webpack-plugin');
module.exports = {
// entry: "./app.js",//单入口写法,入口指定为app.js
entry: { // 多入口写法:入口名称+入口文件
app1: ["./app.js"],
app2: "./app2.js"
},
// entry: ["./app.js", './app2.js'] // 两个文件同时作为一个入口
output: {
path: __dirname + '/dist', // 绝对路径,__dirname是node的全局变量,表示当前目录的绝对路径
filename: "[name].[hash:4].bundle.js", //将name加到filename里,打包结果文件是app.bundle.js和app2.bundle.js,hash是对文件是否有改变的标志,:4表示截取前4位
},
mode: "development", //webpack4以后要指定mode
// loader
module: {
rules: []
},
plugins: [
new htmlwebpack({
template: './index.html',// 写法1,指定html模板
// templateContent: function () { // 写法2,自定义模板,使用频率较低
// return '<div>123</div>'
// }
filename: "index.html",
chunks: ["app1"],
minify: {
collapseInlineTagWhitespace: false, // 是否一行展示
removeComments: false, // 是否移除注释
removeAttributeQuotes: false, // 是否移除属性之间的多余空格
},
inject: "body" // 指定js加载位置: body|true(加载到body中), head, false(不加载)
}),
new htmlwebpack({
template: './index2.html',
filename: "index2.html",
chunks: ["app2"]
})
],
optimization: {
splitChunks: {
chunks: "all", // 分割规则:all, async(只拆分异步),initial(只拆分同步)
minChunks: 2, // 一个chunk重复出现几次才拆分为独立的文件
minSize: 0 // 最小拆分量(拆分量多了会增加http请求数量,造成资源浪费),单位bit
}
}
}
3 打包
只有1个a.js被打包。
app.js和app2.js都不再有a.js的内容。
4 其他配置
多入口文件命名
optimization的splitChunks的属性name值为要指定的文件名。
// webpack.config.js 局部
optimization: {
splitChunks: {
chunks: "all", // 分割规则:all, async(只拆分异步),initial(只拆分同步)
minChunks: 2, // 一个chunk重复出现几次才拆分为独立的文件
minSize: 0, // 最小拆分量(拆分量多了会增加http请求数量,造成资源浪费),单位bit
name: "a"
}
}
8.4 指定要分割的文件
需要将某个文件单独分割出来,或将第三方库单独打包,runtime代码等单独打包。
这是单入口和多入口通用的功能。
p7 22:00
不看了,多入口的情况用不到,现在碰到的项目还是vue居多,用到再看。