webpack加载vue-Webpack异步加载原理及包传递策略

原来的:

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[], callbackfunction(require), chunkNameString)

我们打开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'),
},
],
})

加载中_webpack加载vue_加载中的图片

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等。此时页面尤其是首页的加载性能得到极大的优化。

终于