记一次前端首屏加载的优化
前端优化首屏加载时间
我接手vue2的一个旧项目,因为首屏加载很慢,要求我对此进行优化。我在此进行记录
一、分析加载慢的原因
推荐使用webpack-bundle-analyzer
插件,可以对打包后的文件大小进行可视化分析。简单好用
安装插件
1
npm install webpack-bundle-analyzer --save-dev
在
vue.config.js
文件中添加对应配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
publicPath: '/',
lintOnSave: true,
productionSourceMap: false,
filenameHashing: true,
configureWebpack: (config) => {
const plugins = []
// 配置BundleAnalyzerPlugin
plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888, // 运行后的端口号
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info',
})
)
return { plugins }
}
}配置完成后执行
npm run build
则自动打开页面展示文件大小。如下图:
可以看到优化前node_modules
模块占据了整个页面的将近70%大小(页面中占据面积越大的,也就是打包后体积越大的),其中显眼的插件有tinymce
、xlsx
、avue.min.js
、theme.js
、swiper.js
、html2canvas.js
后面优化只要对症下药即可。优化掉对应的插件。网上提供的思路是使用cdn进行优化。下面进行详细记录
二、使用CDN进行优化
使用cdn优化即需要使用外链引入对应插件,那么就需要在index.html
文件中添加对应的引入链接。然后需要在打包的时候把通过外链引入的插件排除在外,不进行打包。这里推荐使用这个网站搜索你要的插件,进行CDN外链引入
https://www.jsdelivr.com/
index.html
引入外链在
index.html
文件中添加如下代码:1
2
3
4
5
6
7
8
9<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>配置不打包对应的插件
在
vue.config.js
文件中添加以下配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82// 配置不打包的模块
const externals = {
// 左边是引入的参数,例如 import Vue from 'vue' 这里面的 'vue'
// 右边是CDN模块的导出对象字段
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
'@smallwei/avue': 'AVUE',
'element-ui': 'ELEMENT',
xlsx: 'XLSX',
tinymce: 'tinymce',
html2canvas: 'html2canvas',
echarts: 'echarts',
}
// CDN外链,会插入到index.html中
const cdn = {
// 开发环境
dev: {
css: [],
js: [],
},
// 生产环境
build: {
css: [],
js: [
// cdn.jsdelivr.net域名下的cdn文件
'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.2.0/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/index.js',
'https://cdn.jsdelivr.net/npm/@smallwei/avue@2.6.18/lib/avue.min.js',
'https://cdn.jsdelivr.net/npm/xlsx@0.17.5/xlsx.js',
'https://cdn.jsdelivr.net/npm/tinymce@5.10.2/tinymce.min.js',
'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.js',
'https://cdn.jsdelivr.net/npm/echarts@4.8.0/dist/echarts.min.js'
],
},
}
module.exports = {
publicPath: publicPath,
lintOnSave: true,
productionSourceMap: false,
filenameHashing: true,
configureWebpack: (config) => {
const plugins = []
// 配置BundleAnalyzerPlugin
plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888, // 运行后的端口号
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info',
})
)
if (process.env.NODE_ENV === 'production') {
// externals里的模块不打包
config.externals = externals
}
return { plugins }
},
chainWebpack: (config) => {
// 添加自定义参数cdn
config.plugin('html').tap((args) => {
if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build
} else {
args[0].cdn = cdn.dev
}
return args
})
},
}完成以上配置后,再运行
npm run build
得出以下结果:
可以看到优化前node_modules
模块打包后占了12.48M,优化后nodee_modules
模块打包后占了4.02M。
首屏加载的时间从原来的5s变成了3s,还有很多插件也可以使用cdn进行优化,只需要将上图占面积大的插件进行优化即可。另外,由于老项目,经手了很多人,太多重复性代码,所以上图左边views
模块占了很大空间。漫漫优化路,无穷无尽。
/————————————————————– 分割线 ————————————————————————/
三、路由懒加载
按照上面的优化思路实现后发现还是不够快,需要更进一步优化。分析其原因,发现在加载首页的时候将打包后的所有的js文件,css文件都引入了。这就会导致花费了大量时间加载不必要的文件。欸,没错。这时就需要使用路由懒加载进行拆分,只加载当前页面需要的js和css文件,减少不必要的文件请求。
网上可以查到多种不同的懒加载方式,这里使用官方介绍的把组件按组分块来进行路由懒加载
1 | const UserDashboard = () => import(/* webpackChunkName: "group-user" */ './UserDashboard.vue') |
配置不同的webpackChunkName
就可以将对应的组件打包进对应文件中,轻松实现分块打包。
原以为这就结束了,但是打包后一看index.html
文件,发现事情不简单
打包确实分块了,但是入口文件还是将所有的js和css文件都引入了,这是为什么?
原因是vue-cli3 默认会把所有通过import()按需加载的javascript文件加上 prefetch,也就是上图中link
标签后面的属性rel=prefetch
prefetch链接会消耗宽带,因此我们要将prefetch
手动关掉,使入口文件只有必要的js和css文件。
关闭prefetch
代码如下:
1 | // vue.config.js |
再进行打包,观察入口文件结果如下:
完美!至此完成路由懒加载~
四、插件按需引入
这一块不用多说,像ElementUI
、echarts
等等,体积较大的插件,如果只用到其中的几个模块但全局引入了,会导致文件过大。
echarts
的按需引入直接参照官网即可:
https://echarts.apache.org/handbook/zh/basics/importelementUI
的按需引入过程中,配置中会存在一点小问题。
如果脚手架比较新没有.babelrc
文件,则修改babel.config.js
文件,添加配置项:
1 | { |
还有一个小问题就是element的隐藏组件scrollBar并没有对外暴露,这个组件无法按需引入。
五、压缩图片
至此首页快了非常多,观察首页的所有请求,发现还有两张图片请求较大,便将图片进行压缩(原来的我以为图片压缩会导致像素变低,直到我在网上尝试了压缩图片,压缩前后的观感完全一样,但是文件大小直接降低了近70%。不过大部分压缩图片的网站是需要会员或者收费的)
下面是我常用的一个免费的压缩图片的网站:
https://kt.fkw.com/yasuo.html?_ta=8780
压缩完图片进行替换,请求再次变小,速度再次提升。
最后
最后如果服务器有nginx
代理的话,可以配置gzip
压缩传输,速度会更快,但是这里服务器没有使用nginx
代理,故不能配置gzip
了,不然我想这个网站肯定可以更快