前言
性能优化那么重要吗?目前为止,开发移动端活动h5页面有快一年的时间,发现当用户打开你的活动,如果白屏很久首屏加载很慢,用户在漫长的等待中依旧看不到活动页面,直接就会退出去。在这个快准狠的时代,折损率是必须要考虑的问题。而性能的好坏直接影响到用户的体验。
2020年应届,目前开发了多种类型的活动。最开始只是图片处理,但是经过快一年时间,解决问题的思路有所改变。比如前端优化,应该从全局思考每个阶段做了什么,找到是否有可以提高性能的手段。目前总结出比较常用且通用的方法。
思考
1.地址栏中输入url,敲击回车到一个网页呈现在你眼中,到底发生了什么?
2.浏览器从加载到渲染到底做了什么?
3.如何全链路优化性能?
4.优化的参考指标有哪些?
内容
一、输入url的流程
1.DNS发起域名解析,会在缓存里先去查找是否有改域名的对应ip信息,如果没有,会递归的方式层层递归,直到找到需要的ip地址。
2.获得到对应的ip地址后,发送http请求。这里不得不提到CDN(内容分发网络),CDN就是就近访问。DNS服务可以根据LDNS来进行定位,调度服务器判断是来自某地的LDNS的服务器,调度服务器就会去访问该地的CDN服务。这样就能让用户访问到最优的cdn节点。CDN有缓存,责任直接返回资源。如果没有则向上访问直到找到文件会到达源站。并缓存该资源。服务器响应http请求,并返回对应的资源(html)
3.根据渲染树,浏览器或者是移动端的webView,绘制页面也最终呈现给用户。
二、加载到渲染的流程
三、性能优化:
1.网络请求
1)http2的多路复用
http1.1 中华人民共和国外交部网站
http2.0 天猫官网
对比HTTP/1.0 http/1.1 http/2.01)文件大小
代码的压缩,构建时比如空格、注释\console.log;减少文件体积
http/1.0: 每个TCP链接只能发送一个请求。数据发送完毕后,立即断开链接。如果需要请求其他资源就需重新建立新的链接。
http/1.1 引用了持久链接,TCP默认不关闭,可以多个请求复用。同时还引入了管道机制,允许一个TCP链接里面,同时发送多个请求。
但是,数据的通讯是有顺序的。必须一个处理完,才会进行下一个的回应。
http/2.0 帧与流。同时发送多个请求和响应,对同一个请求或响应的所有数据包做了一个独一无二的标识,所以可以不用等待发送。两端会根据标识组装数据流。
2)gzip资源上传
前端打包压缩可以减少服务器压缩的时间,减少服务器的开销。在webpack中可以配置,开启构建时打包
const FileManagerPlugin = require('filemanager-webpack-plugin'); // webpack zip
3)减少http请求的次数(精灵图)
4)上传到CDN
5)按需加载
使用webpack将第三方引入分开打包,改用末端js直接引入
//告知第三方引用库不加入webpack打包, 在webpack的配置文件中
externals: {
'phaser': 'Phaser'
}
6)预加载与懒加载
第二屏的预加载 以及第一屏非首屏部分懒加载
预加载:在App.vue mouted中直接使用
function preLoading(arr,delay,callback) {
setTimeout(() => {
for(let imgUrl of arr) {
let im = new Image();
im.src = imgUrl;
im.onload = () => {
callback()
};
}
}, delay);
}
//调用预加载
preLoading([ '//a.com/again/one.png.webp', '/a.com/again/two.png.webp'],1000,()=>{console.count()})
js懒加载:使用vue-lazyLoad:当滚动条滚动到视口可以展示出元素的时候,再请求该图片资源
2.文件加载
1)文件大小
代码的压缩,构建时比如空格、注释\console.log;减少文件体积
webpackconfig =
{ ...
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
minify: {
removeComments: true, //移除注释
collapseWhitespace: true, //移除空格
removeAttributeQuotes: true //移除属性上的引号
},
}),
]
}
//去掉console.log
webpackconfig = {
...
optimization: {
minimize: true,
minimizer: [
...
new TerserPlugin({
terserOptions: {
sourceMap: true,
compress: {
drop_console: process.env.NODE_ENV === 'production'
//生产环境删除console.log
},
format: {
comments: false //构件时去除注释
}
},
extractComments: false //单独剥离注释
})
],
}
}
2)图片大小
TinyPNG压缩(tinypng.com/) webp后缀、移动端使用设计稿75%展示、背景图片重复单元平铺使用
注明:webp是一种图片的格式,可以在保证图片的质量,根据一定的算法,将图片的大小压缩,在移动端是很好一种图片处理的方式。
3)首屏资源大小
移动端首屏资源最好打包后控制1M
3.页面渲染
1)js滞后
第三方引用js放在main.js 尾部
2)模糊图片先渲染后清晰图片替换
img.src = blur.png;
img.onload = ()=>{
img.src = null;
//替换成清晰图片
loadClearPic(img,clearPic);
}
const loadClearPic = (obj,clearUrl)=>{
let imgObj = new Image();
imgObj.src = clearUrl;
imgObj.onload = function () {
obj.src = clearUrl;
};
}
3)减少DOM的嵌套
4)防止多次渲染(防抖与节流)
防抖:比如监听滚动的时候会一直执行scroll函数,采用防抖,在执行滚动的一定时间后再进行打印(应用场景:疯狂点击,滚动页面)
function debounce(fn, delay) {
let timer = null; //借助闭包
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, delay);
};
}
function showTop() {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log("滚动条位置:" + scrollTop);
}
window.onscroll = debounce(showTop, 1000);
节流:当用户一直在滚动页面,也会在控制台中定时间执行scroll函数(应用场景: 搜索框实时查询,浏览器页面resize)
function showTop() {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log("滚动条位置:" + scrollTop); }
//节流
function throttle(fn,delay) {
let timer = true;
return function() {
if(!timer) {
return false;
}
timer = false;
setTimeout(()=>{
fn();
timer = true;
},delay)
}
}
window.onscroll = throttle(showTop,1000)
5)服务器渲染
服务端直接渲染生成页面上的内容,浏览器直接显示服务端返回的html
4.代码书写
1)样式优化
a.样式继承、采用类名增删而不是逐一更改样式
b.避免回流减少重绘
回流:更改布局后就会造成回流
重绘:例如颜色背景的修改,不涉及布局
注意: 一般自适应布局,多使用display:flex,比float性能好
c.使用css3动画代替帧动画
d.复杂动画在不影响视觉的时候可以减帧展示
2)脚本优化
a.先处理数据后一次性修改Dom
b.减少对Dom的频繁操作,比如Vue是数据驱动的框架
四、性能指标参考
1.lighthouse
chrome插件,一般优化在评分为80以上,是一个有消耗性能的页面
2.report.js
webpack中可以实现构建时生成性能报告,后续针对哪个包体大,逐一优化。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
3.performance
总结
以上是一些h5页面的优化方式,但是具体的问题还需要具体分析。
然,目前学习还是很浅显的,很多时候还是处在一种会使用,遇到问题就百度的阶段。虽然可以快速处理遇到的问题,但是整体的知识体系还是很零散。所以利用一些时间,重新回顾一下自己。
最后想说,学习是一个循序渐进的过程,不能急于求成。很多时候,我们是需要一个停下来并回头看看的过程的。