Vue打包优化

前言

Vue项目打包后JS文件特别大?

怎么解决呢?

http1.1下面分割的chunk也不宜太多,因为在同一个域下面的资源并发下载的资源数量是有限的,比如chrome大约是6个资源同时下载,这6个资源中必须要有某个资源下载完毕后才会下载其他资源,但也不一定非要少于6个,因为某些资源可能直接读取内存,某些资源可以通过cdn从别的域中读取。

实现方式

不生成SourceMap

VueCli

在 vue.config.js 中配置

production环境不生成SourceMap

1
2
3
4
module.exports = {
lintOnSave: false,
productionSourceMap: process.env.NODE_ENV !== "production", //打包不生成map文件
}

WebPack

在 webpack 配置中配置:

1
2
3
module.exports = {
devtool: 'none' // 或者 false
}

externals

externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法。

1、引入链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>标题</title>
</head>

<body>
<div id="app"></div>
</body>

<script src="/librarys/vue@2.6.11/vue.min.js"></script>
<script src="/librarys/vue-router@3.2.0/vue-router.min.js"></script>
<script src="/librarys/vuex@3.2.0/vuex.min.js"></script>
<script src="/librarys/axios@0.21.1/axios.min.js"></script>
<style>
body{
margin: 0;
padding: 0;
}
</style>
</html>

2、配置 externals

1
2
3
4
5
6
7
8
9
module.exports = {
//...
externals: {
"vue": "Vue",
"vue-router": "VueRouter",
"vuex": "Vuex",
"axios": "axios",
},
};

其中引用更推荐CDN引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title></title>
<!-- 引入 cdn 地址 -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.10/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>

路由动态引用

异步引入的块,也就是import('')方式引入的资源,包括异步模块,路由加载等,会生成单独的chunk。

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: '/page1',
component: () => import('@/views/Page1.vue')
},
{
path: '/page2',
component: () => import('@/views/Page2.vue')
}
]

这会使 Page1.vue 和 Page2.vue 分别打包到单独的 chunk 中,访问对应路由时按需加载。

设置生成文件名称

1
2
3
4
5
6
{
path: "/login",
name: "Login",
component: () =>
import(/* webpackChunkName: "login" */ "../views/Login.vue"),
},

实际会生成login.1681894249346.js文件。

图片

VueCli

在 Vue 项目中,可以通过 vue.config.js 中的 chainWebpack 配置来设置图片转 Base64 的大小限制。

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => Object.assign(options, {
limit: 10000 // 小于10k的图片转为base64格式
}))
}
}

这里使用 url-loader,将小于 10000 字节(约 10kb)的图片转为 base64 格式,超过的图片仍使用 file-loader 进行 copy 和 url-replace。

WebPack

安装url-loader

1
npm i url-loader@4.1.1 file-loader@6.2.0 -D

在webpack.config.js中添加处理url路径的loader模块:

1
{test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?esModule=false&limit=500&name=imgs/[hash:8]-[name].[ext]'},

上面这种输入参数的方式还有另一种方式,以对象的键值对方式,如下:

1
2
3
4
5
6
7
8
9
10
11
{
test: /\.(jpg|png|gif|bmp|jpeg|jfif)$/,
use: [{
loader: 'url-loader',
options: {
esModule: false,
limit: 500, //是把小于500B的文件打成Base64的格式,写入JS
name: 'imgs/[hash:8]-[name].[ext]' // [hash:8] 在名称前面设置8位哈希值,[name] 设置文件的原名, [ext] 设置文件的原后缀
}
}]
},// 处理 图片路径的 loader

对比

file-loader 可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存。此外,这意味着 你可以就近管理图片文件,可以使用相对路径而不用担心部署时 URL 的问题。使用正确的配置,webpack 将会在打包输出中自动重写文件路径为正确的 URL。

url-loader 允许你有条件地将文件转换为内联的 base-64 URL (当文件小于给定的阈值),这会减少小文件的 HTTP 请求数。如果文件大于该阈值,会自动的交给 file-loader 处理。

Tree Shaking

使用 Tree Shaking:这可以移除未使用的代码,减小 bundle 大小。

Tree Shaking是指在打包时,只将使用的代码打包进最终的输出文件,未使用的代码被自动剔除,从而减小输出文件的大小。

VueCli

Vue CLI的Tree Shaking功能默认是开启的,不需要额外的配置。

WebPack

Webpack中Tree Shaking主要有以下配置:

  1. 设置mode为production,因为Tree Shaking只在生产环境下起作用。
1
2
3
4
module.exports = {
mode: 'production',
// other configurations
}
  1. 在optimization配置中设置usedExports为true。此选项告诉Webpack检测哪些导入导出被使用,在生成的代码中只包含被使用的代码,从而减少生成的代码体积。
1
2
3
4
5
6
7
8
const webpack = require('webpack');

module.exports = {
// ...other configurations
optimization: {
usedExports: true
}
};
  1. 如果你使用的是ES6的模块语法,确保在package.json中设置了sideEffects属性。这可以帮助Webpack确定哪些模块没有副作用,可以进行Tree Shaking。

    副作用是指模块执行时会导致除了导入导出之外的行为,比如在模块中修改全局变量、执行HTTP请求等。

1
2
3
4
5
6
7
{
"name": "example-project",
"version": "1.0.0",
"description": "Example project for demonstrating Tree Shaking in Webpack",
"main": "src/index.js",
"sideEffects": false // 或者设置为数组指定哪些模块有副作用
}
  1. 使用UglifyJS插件或Terser插件等压缩插件进行代码压缩,减少生成的代码体积。
1
2
3
4
5
6
7
8
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
// ...other configurations
optimization: {
minimizer: [new UglifyJSPlugin()]
}
};

当你完成以上配置后,Webpack会自动启用Tree Shaking功能,对未使用的代码进行消除,从而减少生成的文件体积。

查看VueCli生成的Webpack配置

查看vue-cli中配置项最终生成的webpack配置
在package.json中添加

1
2
3
"scripts": {
"inspect": "vue inspect > webpack.inspect.json",
}

执行一下 npm run inspect 那么就可以生成一个webpack.inspect.json的文件

设置生成文件的名称

VueCli

1
2
3
4
5
6
7
8
9
10
11
const Timestamp = new Date().getTime();
module.exports = {
productionSourceMap: process.env.NODE_ENV !== "production",
configureWebpack: {
output: {
// 输出重构 打包编译后的 文件名称 【模块名称.版本号.时间戳】
filename: `js/[name].${Timestamp}.js`,
chunkFilename: `js/[name].${Timestamp}.js`,
},
},
};

依赖大小分析

VueCli

在 Vue CLI 项目中,可以很方便地使用这个工具。具体步骤如下:

  1. 安装依赖
1
npm install webpack-bundle-analyzer -D
  1. 在 vue.config.js 中配置插件
1
2
3
4
5
6
7
8
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
chainWebpack: config => {
config.plugin('webpack-bundle-analyzer')
.use(BundleAnalyzerPlugin)
}
}
  1. 运行打包分析
1
npm run build --report

构建完成后,会自动打开浏览器端口(默认8888),展示 bundle 分析报告。

WebPack

借助插件webpack-bundle-analyzer我们可以直观的看到打包结果中,文件的体积大小、各模块依赖关系、文件是够重复等问题,极大的方便我们在进行项目优化的时候,进行问题诊断。

1、安装

1
npm i -D webpack-bundle-analyzer

2、配置插件

1
2
3
4
5
6
7
8
9
10
11
12
// 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
// ...
plugins:[
// ...
// 配置插件
new BundleAnalyzerPlugin({
})
],
};

3、修改启动命令

1
2
3
"scripts": {
"analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
},

4、执行编译命令

1
npm run analyzer

打包结束后,会自行启动地址为 http://127.0.0.1:8888 的 web 服务

如果,我们只想保留数据不想启动 web 服务,这个时候,我们可以加上两个配置

1
2
3
4
new BundleAnalyzerPlugin({
analyzerMode: 'disabled', // 不启动展示打包报告的http服务器
generateStatsFile: true, // 是否生成stats.json文件
})

这样再次执行打包的时候就只会产生 state.json 的文件了