最近做了报名页的第三期, 就顺手对该项目进行了一些优化. 先看 h5 报名页 https://hd.huya.com/h5/register/index.html?id=225 优化前后性能的对比吧:
优化后
优化前
.
优化前首屏的秒出率大概是 30% 左右, 优化后的首屏秒出率大概是 70% 左右. ps: 因为主要是对个人想法的一些验证, 所以没并没有使用客户端的 use304cache 和龙哥做的 service worker 缓存.
项目背景
该页面位于虎牙直播, 虎牙助手, 虎牙手游的活动中心中. 由 运营同学在管理后台配置所需要的表单字段(单选, 多选, 输入框, 多行文本框, 上传图片等), 生成对应的报名表单. 用户填写表单报名. 该项目的优化基于龙哥开发的脚手架.
其实加快首屏时间有两个关键词, 一个是加载, 一个是渲染. 加快首屏时间就是要减少渲染首屏所需的文件数量和文件尺寸和尽早执行首屏渲染的逻辑. 下文所讲述的方法也是围绕着这两点.
精简代码
emmmm ... 这个好像是句废话, 不过却相当重要, 移动端的网络条件较差, 文件大小对加载速度还是十分有影响的. 尤其是 CSS, 改造旧项目的时候, 光冗余的 css 就删除了将近 300 行. 举几个栗子:
了解元素的默认属性, 比如块级元素不用再设置 width:100%
设置了绝对定位, 浮动的内联元素不用再设置 display:block
注意样式的继承关系, 像 文本相关属性 (颜色, 字体种类, 缩进, 行高等) 列表相关属性 (list-style) 等可以继承的属性, 在父级元素设置了之后, 子元素就不用重复设置该属性了
另外还包括简化 dom 结构, 减少元素间嵌套.
首屏 css 内联
移动端一个不可忽略的事实是 移动端的文件的缓存命中率比较低. css 文件一般在 head 标签里进行引入, 外链的 css 文件会阻塞 HTML 页面渲染, HTML 页面会被继续下载, 阻塞点后面的标签会继续被解析, img,link 等会继续发送请求获取外部资源, 但不会合成 Rendering Tree 或不会触发页面渲染, 也不会执行 JavaScript 代码. 因为使用 fis3 进行构建, 在 css 文件后面加上?__inline 即可.
<link rel='stylesheet' href='index.scss?__inline'>
异步加载非首屏模块
在本项目中, 非首屏的模块我划分出来了两个.
一个是报名成功后的状态展示. 如下图
报名成功后状态
一个是表单校验逻辑 模块.
上述两个模块均可以在渲染完表单之后再进行加载. 这个时候有的同学说了, 报名成功状态异步加载我没意见, 但是表单检验模块你异步加载, 万一用户在该模块还未加载完成的时候就点击提交开始进行校验, 那岂不是要报错了吗.
莫慌, 那就把这部分用户考虑进去, 伪代码如下:
- var valiator;
- function loadValiatorMod(cb) {
- require.async('valiator.js', (mod)=> {
- valiator = mod;
- cb && cb();
- })
- }
- loadValiatorMod();
- $saveInfo.on('click', ()=> {
- if(!valiator) {
- loadValiatorMod(()=>{
- valiator.start();
- // 提交表单
- })
- }else{
- valiator.start();
- // 提交表单
- }
- })
一开始就让
按需加载
如上所说, 该项目要同时适配虎牙直播和助手手游 APP, 那么可以去根据不同的平台去加载一些特性代码. 伪代码如下:
- var isAnchorApp = util.getUrlParam('isAnchorApp') === 1; // 是否是开播 app
- if(isAnchorApp ){
- loadJs('huyasdk.js', function() {
- writeCookie();
- })
- }else{
- loadJs('anchorApp.js');
- }
报名表中可能有上传图片的操作, 上传图片的需要之前升泽写的一个插件, 但是不是每一个报名表所必须的, 当有上传图片的表单再去加载这个插件
- renderFirstScreen(); // 在渲染逻辑执行完之后再判断是否加载上传组件
- var needUploadImg = $('.upload-img').length> 0;
- needUploadImg && loadJs('upload.js');
其他常规操作(不具备通用性)
jquery 版本由 1.8 版本升级到 2.x 版本, 包体积减小
去除引入 es6 的 polyfill 文件, 龙哥帮忙改造了构建工具, 可以对 es6 的一些新特性的进行针对性的 polyfill.
延迟加载. 对性能上报 js 做了延迟加载的处理
上了 https.
在优化本项目的时候, 发现由客户端同学维护的 js sdk 并没有做 gzip 压缩. 让运维同学帮忙处理之后, js sdk 的大小由 17kb 减少至了 6kb.
其实, 还可以有其他方面可以做, 等下次报名页优化的时候再做吧~:
首屏关键 js 内联
这个我们要对现有的 js 代码进行拆分, 使得拆分后首屏的 js 只包括如下三个部分的代码:
请求相关, 渲染相关, 控制非首屏模块加载逻辑.
node 中间层
理论上也是可行的. 在前端这边把请求相关和渲染相关的 js 单独拎出来. 在 node 这边使用 node 试用 ajax 库和模板引擎进行替换, 拼接好 html 和内联的 css 和 请求, 渲染相关的 js 后, 一起返回.
公用库替换
纵观现在组里的 h5 项目, 复杂一些的项目还是比较少的. 依据之前的经验, 其实我们使用 jquery/zepto 的话, 使用频率最高的功能是 ajax, selector, event 这三个. 其实可以把这些模块给封装在一起, 而不是全部使用.
当然, 纸上得来终觉浅, 上述的几个方案还是要自己去试, 有数据支撑后才能证明方案的正确性和可实施性.
来源: http://www.jianshu.com/p/c2d19b95b004