上一篇讲了PC端的部分:前端性能优化(PC端),这次继续说移动端的。相对于PC端的,移动web浏览器上有一些明显的特点:设备的屏幕小、新特性兼容性较好、支持一些比较新的HTML5和CSS3、需要与Native应用交互等。但移动端可用的CPU资源和网络资源极为有限,因此要做好移动端web上的优化往往需要考虑做更多的事情。首先在移动web的前端页面渲染中,PC的优化规则同样适用,此外针对浏览器也要做一些更细节的优化达到更好的效果。需要注意的是,并不是移动端的优化在PC端不适用,而是由于兼容性的原因,一些优化规则在移动端更具有代表性,所以也是为什么我把DNS预解析,离线缓存,HTTP2协议,GPU加速等东西放到这部分来讲...
和上部分一样,只是提供索引,涉及的知识点比较多请自行搜索
网络加载类
1. 首屏数据请求提前,避免JavaScript文件加载后才请求渲染
为了进一步提示页面加载速度,可以考虑将页面的数据请求尽可能提前,避免在JavaScript文件加载完成后才去请求数据。通常数据请求是页面内容渲染中关键路径最长的部分,而且不能并行,所以如果数据请求能提前的话,可以极大程度上缩短页面内容的渲染完成时间。
2. 首屏加载和按需加载,非首屏内容滚屏加载,保证首屏内容最小化
由于移动端网络相对较慢,网络资源有限,因此为了保证尽快完成页面内容的加载,需要保证首屏加载资源的最小化,非首屏的内容使用滚动的方式异步加载。一般推荐移动端页面首屏数据展示延迟不超过3秒。
3. 模块化资源并行下载
主要指模块化JavaScript资源的异步加载,例如AMD的异步模块,使用并行的加载方式能够缩短多个文件资源的加载时间。
4. inline首屏必备的CSS和JavaScript
通常为了在HTML加载完成时能使浏览器中有基本的样式,需要将页面渲染时必备的CSS和JS通过script或style的方式内联到页面中,避免页面HTML载入完成到页面内容展示这段过程中页面出现空白
5. meta dns prefetch设置DNS预解析
设置文件资源的DNS预解析,能让浏览器提前解析获取静态资源的主机IP,避免等到请求的时候才发起DNS解析。
<!-- cdn域名预解析 -->
<meta http-equiv='x-dns-prefetch-control' content='on'>
<link rel="dns-prefetch" href="//x.autoimg.cn">
6. 资源预加载
首屏加载完成后可能会使用的资源,我们可以用 link标签声明特定文件的预加载
<link rel='subresource' href='main.css'>
<link rel='prefetch' href='secondary.js'>
注意:只有可缓存的资源才进行预加载,否则浪费资源!
7. Pre render预渲染
预渲染意味着我们提前加载好用户即将访问的下一个页面,否则进行预渲染这个页面将浪费资源,慎用!
<link rel='prerender' href='//j.autohome.com.cn'>
8. 合理利用MTU策略
通常情况下,TCP网络传输的最大传输单元(MTU)为1500B,即一个RTT(Round-Trip Time,网络请求返回时间)内可以传输的数据量最大为1500字节(为什么以太网mtu值被设定为1500 - 知乎)。因此在前后端分离的开发模式中,尽量保证页面的HTML内容在1KB以内,这样整个HTML内容的请求就可以在一个RTT内完成,最大限度的提高了HTML载入速度
缓存
1. 合理利用浏览器缓存
除了上一节说到的Cache-Control、Expires、Etag和Last-Modified来设置HTTP缓存外,在移动端还可以使用localstorage等来保存ajax返回的数据,或者使用localstorage保存CSS或JS等静态资源,实现移动端的离线应用,尽可能的减少网络请求,保证静态资源内容的快速加载。
2. 静态资源离线方案
对于移动端或者混合应用,可以设置离线文件或离线包机制让静态资源请求从本地读取,加快资源载入速度,并实现离线更新。这块推荐叶小钗大神的前端优化带来的思考,浅谈前端工程化 可以挑着看。离线资源这块东西太多了,以后有时间单独拿出来写
3. 尝试使用 AMP HTML
AMP HTML 可以作为优化前端页面性能的一个解决方案,使用AMP Component中的元素来代替原始页面元素进行直接渲染 [译]关于谷歌的AMP,你需要知道这些。
图片类
1. 图片压缩处理
移动端通常要保证页面中一切用到的图片都是经过压缩优化处理,而不是以原图的形式直接使用的,因为那样很消耗流量,并且加载时间更长。
2. 使用较小的图片,合理使用base64内嵌图片
在页面使用的背景图片不多且比较小的情况下,可以把图片转成base64编码嵌入到html页面或者CSS文件中,这样可以减少页面的HTTP请求数。需要注意的是,要保证图片较小,一般超过5kb的就不推荐base64嵌入显示了(前端开发中,使用base64图片的弊端是什么? - 知乎)
3. 使用更高压缩比格式的图片
使用具有较高压缩比格式的图片,如webp等。在同等图片画质的情况下,高压缩比格式的图片体积更小,能够更快的完成文件传输,节省网站流量。
![](//x.autoimg.cn/path/photo.webp)
4. 图片懒加载
为了保证页面内容最小化,加速页面渲染,尽可能节省首屏网络流量,页面中的图片资源推荐使用懒加载实现,在页面滚动时动态载入图片。
5. 使用Media Query 或者 srcset 根据不同屏幕加载不同大小的图片
针对不同屏幕尺寸和分辨率,输出不同大小的图片或者背景图能保证用户体验不降低的前提下节省网络流量,加快部分机型图片载入速度,这在移动端非常值得推荐
6. 使用iconfont代替图片图标
iconfont体积较小而且是矢量图,因此缩放不会失真,还可以方便修改图片大小和呈现的颜色,但是需要注意iconfont引用不同webfont格式会有兼容问题。
7. 定义图片大小限制
加载单张图片不建议超过30KB,避免大图片加载时间过长而阻塞页面其他资源的下载。如果用户上传的图片过大,建议设置限制。
脚本类
1. 尽量使用id选择器
选择页面DOM元素时尽量使用id选择器,因为id选择器速度最快
2. 合理的缓存dom对象
对于需要重复使用的dom对象,要优先设置缓存变量,避免每次使用时都要从整个dom树重新查找。
// 不推荐
$("#mod .active").removeClass('active');
$("#mod .not-active").addClass("active");
// 推荐
const $mod = $("#mod");
$mod.find(".active").removeClass("active")
$mod.find(".not-active").addClass("active")
jq 上有很多优化建议,详情搜索JQ 优化
3. 页面元素尽量使用事件代理,避免事件直接绑定
使用事件代理可以避免对每个元素都进行绑定。并且可以避免出现内存泄漏以及需要动态添加元素的事件绑定问题
// 不推荐
$(".btn").click( _ => console.log("hello") )
// 推荐
$(document).on("click", ".btn", _ => console.log("world"))
4. 使用touch代理click
由于移动端屏幕的设计,touch事件和click事件触发之间存在300ms延迟,所以在页面没有touchmove实现滚动处理的情况,可以用touchstart代替元素的click事件,加快页面点击的响应速度,提高用户体验。但同时也需要注意页面重叠元素touch动作的点透问题。
// 不推荐
$("body").on("click",".btn",function(){ console.log(this) });
// 推荐
$("body").on("tap",".btn",function(){ console.log(this) });
// tap为zepto用touch事件封装的
5. 避免touchmove,scroll等连续事件处理
需要对这类高频触发回调的事件设置函数节流(throttle | debounce),避免频繁的事件调用导致移动端页面卡顿
6. 避免使用eval、with,使用join代替连接符+,推荐使用ES6的模板字符串
基本的安全脚本编写问题,尽可能使用高效率的特性来完成这些操作,避免不规范或者不安全的写法。
7. 尽量使用ES6+的特性来编程
ES6+在一定程度上更高效,在chrome59版本对ES6做了深度优化之后,很多特性速度提升了80%左右,这也是未来规范的需要。ES8前段时间也已经落地了,如果你还没有掌握ES6语法的话,抓紧时间学吧...
渲染类
1. 使用viewport固定屏幕渲染,可以加速页面渲染内容
一般认为,在移动端设置viewport可以加速页面的渲染,同时可以避免缩放导致页面重排重绘。
// 利用meta标签对viewport进行控制
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
2. 避免各种形式重排(reflow)重绘(redraw)
页面的重排重绘很耗性能,所以一定要尽可能减少页面的重排重绘,例如页面图片大小变化、元素位置变化这些情况都会导致重排与重绘
3. 使用CSS3动画,开启GPU加速
使用CSS3动画可以设置transform: translateZ(0)来开启移动设备浏览器的GPU图形处理加速。这里安利一波京东凹凸实验室,讲的挺好的,GPU加速是什么
4. 合理使用canvas和requestAnimationFrame
选择canvas或者requestAnimationFrame等更高效的动画实现方式,避免使用settimeout、setInterval等方式直接处理连续动画
5. SVG代替图片
部分情况下可以考虑使用SVG代替图片实现动画,因为SVG格式内容更小,而且SVG的DOM结构方便调整
6. 不滥用float
在DOM渲染树生成后的布局渲染阶段,使用float的元素布局计算比较耗性能,所以尽量减少float的使用,推荐使用固定布局或者flex布局的方式来实现页面的元素布局
7. 不滥用web字体或过多的font-size声明
过多的font-size声明会增加字体的计算大小,而且也没啥必要
架构协议
1. 尝试使用SPDY和HTTP2
在条件允许的情况下可以考虑使用SPDY协议来进行文件资源传输,利用连接复用加快传输过程,缩短资源加载时间。HTTP2在未来也一定会成为主流的
2. 使用后端数据渲染
SSR( Server Side Rendering,服务端渲染)的方式可以加速页面内容的渲染展示,避免空白页面的出现,同时可以解决页面SEO的问题。条件允许的话,SSR是一个很好的实践思路。百度SSP单页式应用性能优化实践,React 同构实践与思考
3. 使用Native View代替DOM的性能劣势
可以尝试使用Native View的MNV* 开发模式来避免HTML DOM性能慢的问题,目前使用MNV* 的开发模式已经可以将页面内容渲染体验做到接近客户端Native应用的体验了。
关于页面优化的常用技术手段和思路主要包括以上这些,有部分遗漏欢迎补充。大家可以根据需要将这些方法应用到自己的项目中去,想全部做到几乎是不可能的,但做到用户可以接受的程度还是很容易的。
软件工程没有银弹,在我们做到了极致优化的同时也需要付出很大的代价,这也是前端优化的一个问题。理论上这些优化都可以实现,但是我们也要懂得权衡,优化提升了用户体验,使数据加载更快,但是项目代码却可能打乱,异步内容要拆分出来,首屏雪碧图可能要拆分2个或更多,项目代码维护成本成倍增加,项目结构也可能变得混乱。任何一部分优化都可以做得很深入,但不一定都值得。在优化的同时考虑性价比,这才是我们作为一名前端工程师处理前端优化时该具有的正确思维。
PS:另外身位一个coder,虽然晚睡是常态了,但每次写东西都是熬到半夜2、3点身体也吃不消啊!!