原来的:
webpack异步加载原理
有人称webpack保证异步加载,有人称其为代码切割。 虽然它独立地将js模块导入到.js文件中,然后在使用该模块时,创建一个脚本对象并将其添加到document.head对象中。webpack加载vue,浏览器会手动向我们发起请求来请求这个js文件,然后编写一个回调函数让请求的js文件做一些业务操作。
举个例子
需求:main.js依赖两个js文件:A.js是点击aBtn按钮后执行的逻辑,B.js是点击bBtn按钮后执行的逻辑。
webpack.config.js,我们先写一下webpack打包配置的代码
const path = require('path') // 路径处理模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 引入CleanWebpackPlugin插件
module.exports = {
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'
document.getElementById('aBtn').onclick = function () {
alert(A)
}
document.getElementById('bBtn').onclick = function () {
alert(B)
}
A.js和B.js的代码如下:
// A.js
const A = 'hello A'
module.exports = A
// B.js
const B = 'hello B'
module.exports = B
此时,我们 npm run build 项目,只打包了两个文件。
可以看到,此时webpack将main.js依赖的两个文件同时打包到同一个js文件中,并在index.html中引入。 不过A.js和B.js都有必须点击相应按钮才能执行的逻辑。 如果用户没有点击对应的按钮,而这两个文件又比较大,会不会导致首页默认加载的js文件过大呢? ,导致主页渲染缓慢? 那么是否可以在用户点击按钮的时候加载对应的依赖文件呢?
webpack.ensure 解决了这个问题。
require.ensure异步加载
接下来我们将main.js改为异步加载方式
document.getElementById('aBtn').onclick = function () {
//异步加载A
require.ensure([], function () {
let A = require('./A.js')
alert(A)
})
}
document.getElementById('bBtn').onclick = function () {
//异步加载b
require.ensure([], function () {
let B = require('./B.js')
alert(B)
})
}
此时我们再次打包,发现多了两个文件,1.index.js和2.index.js。 当我们打开页面时,我们只引入index.js文件。 当点击按钮A时,会引入文件1.index.js,当点击按钮B时,会引入文件2.index.js。 这就满足了我们按需加载的需求。
require.ensure函数是代码分离的分割线,也就是说反弹上面的require就是我们要分离出来的,即require('./A.js'),将A.js分离出来,形成一个 webpack 包。 单独的js文件,其句型如下
require.ensure(dependencies: String[], callback: function(require), chunkName: String)
我们打开1.index.js文件,发现其代码如下
(window.webpackJsonp = window.webpackJsonp || []).push([
[1],
[
,
function (o, n) {
o.exports = 'hello A'
},
],
])
从前面的代码可以看出:
异步加载的代码会保存在全局的webpackJsonp中。
webpackJsonp.push的值,两个参数分别是需要安装的模块对应的id和异步加载文件中存储的需要安装的模块列表。
当满足某些条件时,将执行特定模块中的代码。
import() 按需加载
webpack4的官方文档提供了模块的按需裁剪和加载。 借助es6的按需加载import()方法,可以减少主页包的体积,加快主页的请求速度。 只有需要时才会加载其他模块。 js.
import()的句型非常简单。 该函数只接受一个参数,即引用包的地址,并使用 Promise 风格的反弹来获取加载的包。 代码中 import() 的所有模块都会被打包到一个单独的包中,并放置在存储 chunk 的目录中。 当浏览器运行到这行代码时,会手动请求这个资源webpack加载vue,实现异步加载。
接下来我们将上面的代码改为import()方法。
document.getElementById('aBtn').onclick = function () {
//异步加载A
import('./A').then((data) => {
alert(data.A)
})
}
document.getElementById('bBtn').onclick = function () {
//异步加载b
import('./B').then((data) => {
alert(data.B)
})
}
此时打包的文件与webpack.ensure相同。
路由延迟加载
为什么需要延迟加载?
对于Vue这样的单页应用,如果没有路由的延迟加载,webpack打包的文件会很大,导致进入首页时加载的内容过多,长时间出现蓝屏。 如果采用路由延迟加载,可以定义页面,在需要的时候加载页面,可以有效分担首页的加载压力,减少首页的加载时间。
Vue路由延迟加载有以下三种形式:
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) => require.ensure([], () => resolve(require('@/components/home')), 'home'),
},
{
path: '/about',
component: (resolve) => require.ensure([], () => resolve(require('@/components/about')), 'about'),
},
],
})
ES6 导入()
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单个文件较大的情况,而这个文件又恰好是网页第一个加载的文件。 这会导致加载时间过长,导致崩溃时间过长,影响用户。 经验。 所以我们需要有一个合理的总承包策略。
CommonsChunk插件
在Webapck4.x版本之前,我们使用CommonsChunkPlugin进行分离。
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: 'initial',
minChunks: 2,
}),
]
我们将以下文件分开并打包
优化.splitChunks
webpack 4最大的变化就是取消了CommonsChunkPlugin,引入了optimization.splitChunks。 如果您的模式是生产模式,那么 webpack4 将手动启用代码分割。
其外部代码拆分策略如下:
虽然在webpack4中会手动启用Code Splitting,但是随着项目的增长,这往往不能满足我们的需求,需要我们进行个性化的优化。
应用领域
我们首先找一个优化空间大的项目来运营。 这是一个后台管理系统项目。 大部分内容是由3-4个后端开发的。 开发周期通常较短,大多数人没有优化意识。 他们只是编写业务代码来完成需求。 随着时间的推移,打包后的文件大小会变大,极大地影响性能。
我们首先使用 webpack-bundle-analyzer 来分析打包后的模块依赖关系和文件大小,以确定优化的方向。
然后我们看一下打包后的js文件
当我看到这两张照片时,我的心崩溃了。 其缺点如下:
抱怨完之后,我们就开始谈正事。 正是因为存在如此多的缺点,我们才能更好地利用它们来验证我们的优化方法的可行性。
分离echart和iview
从前面的分析可以看出,echart和iview文件太大。 这时候我们使用webpack4的optimization.splitChunks来拆分代码,将它们分成单独的文件。 (为了更好的呈现优化效果,我们先去掉xlsx.js)
vue.config.js 更改如下:
chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[/]node_modules[/]/,
priority: 10,
chunks: 'initial'
},
iview: {
name: 'chunk-iview',
priority: 20,
test: /[/]node_modules[/]_?iview(.*)/
},
echarts: {
name: 'chunk-echarts',
priority: 20,
test: /[/]node_modules[/]_?echarts(.*)/
},
commons: {
name: 'chunk-commons',
minChunks: 2,
priority: 5,
chunks: 'initial',
reuseExistingChunk: true
}
}
})
},
此时我们使用webpack-bundle-analyzer来分析一下
打包后的js文件
从这里可以看出,我们已经成功将echart和iview分别分离出来,vendor.js也相应减小了大小。 此外,我们还可以继续提取其他第三方模块。
CDN方式
虽然单独分离出了第三方模块,但是在加载首页或者对应的路由时仍然需要加载这样一个几百kb的文件,不利于性能优化。 这时候我们就可以利用CDN来引入这样的插件或者UI组件库。
在index.html中引入对应的cdn链接
<head>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/styles/iview.css" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.6.8/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/iview.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/xlsx.mini.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/cpexcel.min.js"></script>
</body>
vue.config.js 配置外部
configureWebpack: (config) => {
config.externals = {
vue: 'Vue',
xlsx: 'XLSX',
iview: 'iView',
iView: 'ViewUI',
}
}
删除之前的导入形式并卸载对应的npm依赖包
npm uninstall vue iview echarts xlsx --save
现在我们看一下打包后的情况
打包后的js文件
做得好! 此时基本没有打包大文件,加载首页所需的vendor.js也只有几十kb,我们可以通过CDN引入vue全家桶的一些模块来进一步优化,比如vue -router、vuex、axios等。此时页面尤其是首页的加载性能得到极大的优化。
终于
发表评论