页面加载性能是一个老生常谈的问题,但是却异常重要,尤其在访问量大的商业软件中。但是有很多开发者在开发过程中压根就没有考虑过这个问题。大家在开发业务代码的过程中,也就忽略了这个增加工作量,也不会带来什么直观的工作内容。
写在前面,这里以vue框架为例,基于vue-cli3的开发方式
首先,使用webpack分析工具,查看当前项目的依赖,分析依赖及打包情况,对症下药
安装插件
npm i webpack-bundle-analyzer -D
在vue.config.js中,添加如下配置:
chainWebpack: (config)=>{
/* 添加分析工具*/
if (process.env.NODE_ENV === 'production') {
if (process.env.npm_config_report) {
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
.end()
config.plugins.delete('prefetch')
}
}
...
}
执行命令
npm run build --report
执行成功后,浏览器会打开一个窗口,显示当前依赖的大小及各打包文件情况
结合自己的项目情况,分析依赖的引入及打包情况,有以下几点优化方式
第一,路由懒加载。
查看打包目录中,js文件夹下的chunk-哈希值的文件为采用懒加载形式时生成的文件,一个路由会生成一个文件。
const home= () => import('@/pages/home/index.vue')
第二,使用CDN引入第三方依赖。
比如,直接引入ehcarts会发现占打包文件较大的空间,如果项目没有特殊要求,可以采用CDN的方式引入;其他诸如axios、vue、lodash等都可以采用这种方式。
- 在index.html中引入CDN资源
...
<body>
<div id="app">
</div>
<!-- built files will be auto injected -->
<script src="//cdn.bootcss.com/echarts/4.2.1/echarts.simple.min.js"></script>
</body>
...
- 修改vue.config.js配置文件
module.exports = {
configureWebpack: {
externals: {
'echarts': 'echarts' // 配置使用CDN
}
}
}
externals中的key是用于import,value表示的在全局中访问到该对象,就是window.echarts
在vue中使用echarts的时候无需 import echarts,可直接使用
第三,按需加载第三方类库
比如,项目中使用了 lodash 库,如果不是大量使用里面的方法的话,可以这样引入
import _cloneDeep from 'lodash/difference' // 或者 const _cloneDeep = require('lodash/difference')
const o = _cloneDeep ({a: 1, b: 2})
也可以借助第三方插件的形式,lodash-webpack-plugin和babel-plugin-lodash。
在使用中还是采用原有的 import _ from 'lodash'方式,只是借助插件,在打包时webpack会根据使用的方法按需打包
先安装依赖
npm install lodash-webpack-plugin babel-plugin-lodash -D
上述插件可能部分已经存在于项目中,可以根据实际删除
接着修改 vue.config.js
const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
return{
plugins: [
new LodashModuleReplacementPlugin(), //优化lodash
]
}
}
}
};
附:使用 IgnorePlugin 插件优化 moment.js
const webpack = require('webpack');
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
return{
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // 忽略/moment/locale下的所有文件
]
}
}
}
};
按需引入 element-ui,参见官方文档即可,其他组件库类似
注:如果是按需一次性在main.js中引入,虽然比全部引入要小一些,但是也会一定程度上影响首次加载,这个看项目而行吧。
按需引入后element-ui
小了很多,不过看到文章开头的图上显眼的 table.js
后想到, table
组件只有后台管理页面用到了,不需要全局注册,所以我们删除 main.js
中 Table
和 TablColumn
的引用,并在后台组件中局部注册。
这里处理的思路就是,将按需引入处理到极致。如果是对首屏要求很高,可以采用这种方式,哪里用到哪里才引用,这其实也是平时开发中的一种良好习惯。
chunk.venders.js
。如果是文件为第三方依赖的打包后文件,在做完这些优化之后,会发现这个文件有显现的减小。
第四,打包时去掉sourceMap文件
修改 vue.config.js 配置
module.exports = {
productionSourceMap: false
}
第五,将静态资源使用cdn加载
将项目中的静态资源js css等放在oss服务器或者其他地方,减小服务器压力
第六,开启 gzip压缩
我在项目中启用压缩后,文件大小减少了70%以上,优化效果十分明显。
下图是在简单做了部分优化之后的加载过程(优化开始时忘了截图),耗时8s以上。服务器端配置以 nginx 为例
如果 Nginx 服务器开启 gzip,会将静态资源在服务端进行压缩,压缩包传输给浏览器后,浏览器再进行解压使用,这大大提高了网络传输的效率,尤其对 js,css 这类文本的压缩,效果很明显。
客户端
安装依赖
npm i -D compression-webpack-plugin
修改 vue.config.js 配置
const path = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
...
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
return {
plugins: [
new CompressionPlugin({
test: /\.js$|\.html$|\.css$|\.jpg$|\.jpeg$|\.png/, // 需要压缩的文件类型
threshold: 10240, // 归档需要进行压缩的文件大小最小值,这里是10K以上的进行压缩
deleteOriginalAssets: false // 是否删除原文件
})
]
}
}
}
}
打包后,查看js文件
可以看到所有文件都被压缩了三分之二以上
在服务器我们也要做相应的配置
# 开启|关闭 gzip。
gzip on|off;
# 文件大于指定 size 才压缩,以 kb 为单位。
gzip_min_length 10;
# 压缩级别,1-9,值越大压缩比越大,但更加占用 CPU,且压缩效率越来越低。
gzip_comp_level 2;
# 压缩的文件类型。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
# 开启后如果能找到 .gz 文件,直接返回该文件,不会启用服务端压缩。
gzip_static on|off
# 是否添加响应头 Vary: Accept-Encoding 建议开启。
gzip_vary on;
# 请求压缩的缓冲区数量和大小,以 4k 为单位,32 为倍数。
gzip_buffers 32 4K;
注:遇到服务端开启gzip后,并没有生效的问题,发现是nginx配置压缩文件类型时 application/x-javascript,如果是这样的写法则并不会生效。
JavaScript的MIME类型通常为“application/x-javascript”, 非IE的浏览器认“application/javascript”,
所以在上述配置中 application/javascript 和 application/x-javascript 并用,可以解决该问题。
然后重启nginx服务
systemctl restart nginx.service
当在请求中出现如下标识,即开启成功
再对比一下资源加载时间
前者为启用压缩前,后者为压缩后,时间从8.33s减少到了2.44s,效率提高了70%以上
第七,冗余代码
打包文件 app.哈希.js 中为所有vue文件打包的集合。
基于此,把项目中的冗余代码,注释的多余代码删除一通后,你会发现文件会变小。
可能人就是这样,在项目中觉得多几行css 多几个标签觉得不会对页面产生什么影响,但是如果做一通优化之后看到了‘数字性’的减少,才会思考编写高性能代码对加载性能的影响。
第八,浏览器缓存
浏览器缓存可以分为强缓存和协商缓存,根据实际应用场景来选择缓存方式或者结合使用。一般来讲一些基本不会变化的静态资源文件可以设置强缓存,更新频繁的文件不要设置缓存。而启用缓存的好处在于,在某个时间段内可以减少发送请求的数量,从而使页面响应更快,也就有更好的页面体验。
基本原理:浏览器缓存分强缓存和协商缓存,他们是将网络资源存储在本地,等待下次请求该资源时,如果命中就不需要到服务器重新请求该资源,直接在本地读取该资源。
- 强缓存:在web服务器返回的响应中添加Expires和Cache-Control Header
- 协商缓存:通过【Last-Modified, If-Modified-Since】和【ETag, If-None-Match】两对Header分别管理
关于缓存的详细介绍,推荐一篇文章,时空隧道
好啦,文章提到的优化方式基本就是这些,当然优化也不至于此,还有网络加载优化、页面渲染优化(动画、重排、重绘等)、浏览器文件缓存等等。如果有更好的优化方式欢迎评论。
写在最后,页面优化本身是一件很抽象的工作,但是我们却可以通过平时的编码规范来促成更可靠的页面。优化的过程也是一个见仁见智的过程,要结合实际项目实际分析。优化的过程也会引发我们对于编码时的一些思考,原来这样写对页面加载会更友好,不知不觉中也能促进编写高可用的能力。