Webpack的异步加载原理及分包策略(深度好⽂,建议收藏)作者:lzg9527
webpack 异步加载原理
webpack ensure 有⼈称它为异步加载,也有⼈称为代码切割,他其实就是将 js 模块给独⽴导出⼀个.js ⽂件,然后使⽤这个模块的时候,再创建⼀个 script 对象,加⼊到 document.head 对象中,浏览器会⾃动帮我们发起请求,去请求这个 js ⽂件,然后写个回调函数,让请求到的 js ⽂件做⼀些业务操作。
举个例⼦
需求:main.js 依赖两个 js ⽂件:A.js 是点击 aBtn 按钮后,才执⾏的逻辑,B.js 是点击 bBtn 按钮后,才执⾏的逻辑。
const path = require('path') // 路径处理模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 引⼊CleanWebpackPlugin插件
entry: {
index: path.join(__dirname, '/src/main.js'),
},
output: {
path: path.join(__dirname, '/dist'),
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '/index.html'),
}),
new CleanWebpackPlugin(), // 所要清理的⽂件夹名称
],
}
index.html 代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack</title>
</head>
<body>
<div id="app">
<button id="aBtn">按钮A</button>
<button id="bBtn">按钮B</button>
</div>
</body>
</html>
⼊⼝⽂件 main.js 如下
import A from './A'
import B from './B'
alert(A)
}
alert(B)
}
A.js 和
B.js 的代码分别如下
// A.js
const A = 'hello A'
/
/ B.js
const B = 'hello B'
此时,我们对项⽬进⾏ npm run build, 打包出来的只有两个⽂件
index.html
index.js
由此可见,此时 webpack 把 main.js 依赖的两个⽂件都同时打包到同⼀个 js ⽂件,并在 index.html 中引⼊。但是 A.js 和 B.js 都是点击相应按钮才会执⾏的逻辑,如果⽤户并没有点击相应按钮,⽽且这两个⽂件⼜是⽐较⼤的话,这样是不是就导致⾸页默认加载的 js ⽂件太⼤,从⽽导致⾸页渲染较慢呢?那么有能否实现当⽤户点击按钮的时候再加载相应的依赖⽂件呢?
下⾯我们将 main.js 改成异步加载的⽅式
//异步加载A
let A = require('./A.js')
alert(A)
})
}
//异步加载b
let B = require('./B.js')
alert(B)
})
}
此时,我们再进⾏⼀下打包,发现多了 1.index.js 和 2.index.js 两个⽂件。⽽我们打开页⾯时只引⼊了 index.js ⼀个⽂件,当点击按钮 A 的时候才引⼊ 1.index.js ⽂件,点击按钮 B 的时候才引⼊ 2.index.js ⽂件。这样就满⾜了我们按需加载的需求。
我们打开 1.index.js ⽂件,发现它的代码如下
(window.webpackJsonp = window.webpackJsonp || []).push([
[1],
[
,
function (o, n) {
},
],
])
由上⾯的代码可以看出:
1. 异步加载的代码,会保存在⼀个全局的 webpackJsonp 中。
2. webpackJsonp.push 的的值,两个参数分别为异步加载的⽂件中存放的需要安装的模块对应的 id 和异步加载的⽂件中存放的需要安
装的模块列表。
3. 在满⾜某种情况下,会执⾏具体模块中的代码。
import() 按需加载
webpack4 官⽅⽂档提供了模块按需切割加载,配合 es6 的按需加载 import() ⽅法,可以做到减少⾸页包体积,加快⾸页的请求速度,只有其他模块,只有当需要的时候才会加载对应 js。
import()的语法⼗分简单。该函数只接受⼀个参数,就是引⽤包的地址,并且使⽤了 promise 式的回调,获取加载的包。在代码中所有被import()的模块,都将打成⼀个单独的包,放在 chunk 存储的⽬录下。在浏览器运⾏到这⼀⾏代码时,就会⾃动请求这个资源,实现异步加载。
下⾯我们将上述代码改成 import()⽅式。
//异步加载A
import('./A').then((data) => {
alert(data.A)
})webpack打包流程 面试
}
//异步加载b
import('./B').then((data) => {
alert(data.B)
})
}
此时打包出来的⽂件和 sure ⽅法是⼀样的。
路由懒加载
为什么需要懒加载?
像 vue 这种单页⾯应⽤,如果没有路由懒加载,运⽤ webpack 打包后的⽂件将会很⼤,造成进⼊⾸页时,需要加载的内容过多,出现较长时间的⽩屏,运⽤路由懒加载则可以将页⾯进⾏划分,需要的时候才加载页⾯,可以有效的分担⾸页所承担的加载压⼒,减少⾸页加载⽤时。
vue 路由懒加载有以下三种⽅式
vue 异步组件
ES6 的 import()
webpack 的 sure()
vue 异步组件
这种⽅法主要是使⽤了 resolve 的异步机制,⽤ require 代替了 import 实现按需加载
export default new Router({
routes: [
{
path: '/home',',
component: (resolve) => require(['@/components/home'], resolve),
},
{
path: '/about',',
component: (resolve) => require(['@/components/about'], resolve),
},
],
})
这种模式可以通过参数中的 webpackChunkName 将 js 分开打包。
export default new Router({
routes: [
{
path: '/home',
component: (resolve) => sure([], () => resolve(require('@/components/home')), 'home'),
},
{
path: '/about',
component: (resolve) => sure([], () => resolve(require('@/components/about')), 'about'),
},
]
,
})
ES6 的 import()
vue-router 在官⽹提供了⼀种⽅法,可以理解也是为通过 Promise 的 resolve 机制。因为 Promise 函数返回的 Promise 为 resolve 组件本⾝,⽽我们⼜可以使⽤ import 来导⼊组件。
export default new Router({
routes: [
{
path: '/home',
component: () => import('@/components/home'),
},
{
path: '/about',
component: () => import('@/components/home'),
},
],
})
webpack 分包策略
在 webpack 打包过程中,经常出现 vendor.js, app.js 单个⽂件较⼤的情况,这偏偏⼜是⽹页最先加载的⽂件,这就会使得加载时间过长,从⽽使得⽩屏时间过长,影响⽤户体验。所以我们需要有合理的分包策略。
CommonsChunkPlugin
在 Webapck4.x 版本之前,我们都是使⽤ CommonsChunkPlugin 去做分离
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
return (
/.js$/.source) &&
)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: 'initial',
minChunks: 2,
}),
]
我们把以下⽂件单独抽离出来打包
node_modules ⽂件夹下的,模块
被 3 个 ⼊⼝ chunk 共享的模块
optimization.splitChunks
webpack 4 最⼤的改动就是废除了 CommonsChunkPlugin 引⼊了 optimization.splitChunks。如果你的 mode 是 production,那么webpack4 就会⾃动开启 Code Splitting。
它内置的代码分割策略是这样的:
新的 chunk 是否被共享或者是来⾃ node_modules 的模块
新的 chunk 体积在压缩之前是否⼤于 30kb
按需加载 chunk 的并发请求数量⼩于等于 5 个
页⾯初始加载时的并发请求数量⼩于等于 3 个
虽然在 webpack4 会⾃动开启 Code Splitting,但是随着项⽬⼯程的最⼤,这往往不能满⾜我们的需求,我们需要再进⾏个性化的优化。应⽤实例
我们先到⼀个优化空间较⼤的项⽬来进⾏操作。这是⼀个后台管理系统项⽬,⼤部分内容由 3-4 个前端开发,平时开发周期较短,且⼤部分⼈没有优化意识,只是写好业务代码完成需求,⽇⼦⼀长,造成打包出来的⽂件较⼤,⼤⼤影响性能。
我们先⽤ webpack-bundle-analyzer 分析打包后的模块依赖及⽂件⼤⼩,确定优化的⽅向在哪。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。