背景
最近做了c端的h5产品,上线后,部分用户投诉说打开很慢,经过调查发现,在网速慢的情况下,部分用户首屏展示需要5-10s,网速快的情况也要将近3s。
在对项目做了一些优化处理后,再次无缓存打开可以发现网页几乎是秒开,平均耗时在1s以内
在这里总结记录一下,基本上都是一些常规可复制的优化手段,希望能为同样想优化网页性能的你提供思路~
更多学习案例尽在我的github
这是优化前的效果:
这是优化过后的效果:
关于优化
在开始之前,我们需要明白一个原则:性能优化的最终目的是提升用户体验。
简而言之就是让用户感觉这个网站很「快」(至少不慢hh),这里的「快」有两种,一种是「真的快」一种是「觉得快」
「真的快」:可以客观衡量的指标,像网页访问时间、交互响应时间、跳转页面时间
「觉得快」:用户主观感知的性能,通过视觉引导等手段转移用户对等待时间的关注
做好这两方面都能提升用户对网站的性能评价。
目标
首先我们需要确定目标,根据场景和项目复杂度不同,制定的目标也不同,比如希望比竞品快20%,或者符合标准的"2-5-10"原则等等
这里我定下的目标是
正常网速下,2s内加载完成
指标和检测分析工具
常用指标
关于指标这块,简单介绍下常见指标:
- FCP(First Contentful Paint):白屏时间(第一个文本绘制时间)
- Speed Index:首屏时间
- TTI(Time To Interactive): 第一次可交互的时间
- lighthouse score(performance):Chrome浏览器审查工具性能评分(也可以npm install -g lighthouse,编程式调用)
分析工具
Network分析
Lighthouse分析
Webpack Bundle分析
优化方案
优化体积
优化前 chunk.js 2.3m
优化后 chunk.js 860kb
优化包
⚡ 优化Ant-design-icon体积
这一部分,由于我们在项目中只使用了几个Ant内置图标,不可能有530+KB。根据Ant文档的描述是由于其将ICON全量引入的关系导致的,说法是当前用法如果按需加载的话无法确定使用者会不会在运行时改变icon,比如配置的ICON。
所以决定去掉这部分,改成本地的svg。
优化前:529kb 优化后:20kb
⚡ 删除冗余组件
部分组件是不经常用的,但却使用了Vue.use()全局引入了。这里去掉不常用和没用到的全局引入,改为页面内import()引入
看到ant-design-select, upload只有单独的页面用到。放到页面里面去引用,不放在总包里面了。
优化前:500kb 优化后:0
⚡ 部分库按需引入
由于我们只用了,轮播图和提示窗,mint-ui按需引入。
crypto-js 因为我们只用了一个算法,就按需引入单独的sha256算法
import {HmacSHA256} from ‘crypto-js’
优化前:550kb 优化后:120kb
⚡ 使用webp
webP是谷歌推出的新图片格式(2010),同等质量下体积拳打png脚踢jpg,目前兼容性还算可以,就苹果家的表现不太理想
⚡ 优化core-js体积
core-js实际上就是浏览器新API的polyfill,项目是PC端,所以主要是为了兼容IE…
presets: [“@vue/app”]
改成
presets: [
‘@vue/app’,
[
‘@babel/preset-env’,
{
‘useBuiltIns’: ‘entry’
}
]
],
优化前:104kb 优化后 68kb
网络传输优化
⚡优化分包策略
vue-cli3的默认优化是将所有npm依赖都打进chunk-vendor,但这种做法在依赖多的情况下导致chunk-vendor过大
optimization: isProd ? {
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity, // 默认为3,调整为允许无限入口资源
minSize: 20000, // 20K以下的依赖不做拆分
cacheGroups: {
vendors: {
// 拆分依赖,避免单文件过大拖慢页面展示
// 得益于HTTP2多路复用,不用太担心资源请求太多的问题
name(module) {
// 拆包
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
// 进一步将Ant组件拆分出来,请根据情况来
// const packageName = module.context.match(/[\\/]node_modules[\\/](?:ant-design-vue[\\/]es[\\/])?(.*?)([\\/]|$)/)[1]
return `npm.${packageName.replace('@', '')}` // 部分服务器不允许URL带@
},
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
}
}
},
runtimeChunk: { name: entrypoint => `runtime-${entrypoint.name}` }
} : {}
⚡ Gzip压缩传输和图片打包压缩
图片压缩 并且开启gzip
gzip加速
第一步安装依赖:
npm install --save-dev compression-webpack-plugin
这样会安装最新的版本,如果你webpack版本过低,则会安装失败。
这里就需要降低compression-webpack-plugin版本,这里我使用的是5.0.1版本
npm install --save–dev compression-webpack-plugin@5.0.1
图片资源压缩
npm install --save image-webpack-loader
使用npm安装命令后,安装失败的话需要调整
cnpm --save install image-webpack-loader
这里就会看到gifsicle已经安装好了
const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
chainWebpack: config => {
//最小化代码
config.optimization.minimize(true);
//分割代码
config.optimization.splitChunks({
chunks: 'all'
});
//开启图片压缩
config.module.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ bypassOnDebug: true })
//开启gzip加速
config.plugin('compressionPlugin')
.use(new CompressionPlugin({
test: /\.js$|\.html$|.\css$|\.otf$|\.ttf/, // 匹配文件名
threshold: 102400, // 对超过100kb的数据压缩
deleteOriginalAssets: false // 不删除源文件
}))
},
}
⚡ 预请求预加载
<link>
标签的rel属性的两个可选值。
Prefetch,预请求,是为了提示浏览器,用户未来的浏览有可能需要加载目标资源,所以浏览器有可能通过事先获取和缓存对应资源,优化用户体验。
Preload,预加载,表示用户十分有可能需要在当前浏览中加载目标资源,所以浏览器必须预先获取和缓存对应资源。
⚡ 开启HTTP2
HTTP2是HTTP协议的第二个版本,相较于HTTP1 速度更快、延迟更低,功能更多。
目前来看兼容性方面也算过得去,在国内有超过50%的覆盖率。
通常浏览器在传输时并发请求数是有限制的,超过限制的请求需要排队,以往我们通过域名分片、资源合并来避开这一限制,而使用HTTP2协议后,其可以在一个TCP连接分帧处理多个请求(多路复用),不受此限制。(其余的头部压缩等等也带来了一定性能提升)
如果网站支持HTTPS,请一并开启HTTP2,成本低收益高,对于请求多的页面提升很大,尤其是在网速不佳时
⚡ 托管至OSS + CDN加速
当应对一些弱网地区时,OSS + CDN无疑是很强力的提速手段。
海量,安全,低成本,高可靠的云存储服务。可以通过简单的REST接口,在任何时间、任何地点上传和下载数据,也可以使用WEB页面对数据进行管理。
SSR
想要更快可以使用ssr服务端渲染,关于服务端渲染可以看我的另一篇文章。
这里贴上ssr的首页加载速度,可以看到没缓存的情况下快了几倍。页面直接返回了Html内容。
体验优化
预渲染
预渲染是一个方案,使用爬虫技术。由于我们打包过后的都是一些js文件,使用一些技术(puppeteer)可以爬取到项目在chrome浏览器展示的页面,然后把它写入js,和打包文件一起。类似prerender-spa-plugin
不过这种技术的缺点也很明显,我们在打包过程中,所展示的页面是当时环境下的数据,也就是说如果首页有很多的动态获取的数据(比如说实时的股票价格),那如果采用这种方案,用户第一眼看到并不是当时数据,会认为是个错误信息
骨架屏
首屏优化,APP内常见的加载时各部分灰色色块。针对骨架屏页的自动化生成,业界已有不少解决方案。
渐进加载图片
一般来说,图片加载有两种方式,一种是自上而下扫描,一种则是原图的模糊展示,然后逐渐/加载完清晰。前者在网速差的时候用户体验较差,后者的渐进/交错式加载则能减轻用户的等待焦虑,带来更好的体验
先加载小图,模糊化渲染,图片加载完成后替换为原图,最典型的例子就是Medium,模糊化可以用filter或者canvas处理
结尾
以上就是关于指标检测 和具体优化方案,谈到优化其实还有构建优化,缓存优化等等…
性能优化影响的,不仅是用户体验,还影响了转化率、搜索引擎排名,这些因素都会对最终的流量、销量等收入造成影响
来自Google的数据表明,一个有10条数据0.4秒能加载完的页面,变成30条数据0.9秒加载完之后,流量和广告收入下降90%。
Google Map 首页文件大小从100KB减小到70-80KB后,流量在第一周涨了10%,接下来的三周涨了25%。
亚马逊的数据表明:加载时间增加100毫秒,销量就下降1%。