近期在一家公司负责H5游戏加载速度优化,这里把近期做的项目优化项做一个整理分享:
html渲染流程
HTML解析过程:构建DOM树、构建CSSOM树、根据DOM树和CSSOM树构建render树、有了render树就开始布局Layout、最后绘制paint。
1、构建DOM树: 将HTML构建成一个DOM树,也就是构建节点,把所有的节点都构建出来。
2、构建CSSOM: 解析css去构建CSSOM树。
3、构建render树: DOM已经构建好了,css也有了,浏览器就会根据这两个来构造render树。
4、布局: 当render树有了,通过render树,浏览器开始计算各个节点的位置和样式。
5、绘制: 遍历render树,在页面上绘制每个节点。
6、重排reflow: 当render树绘制完成之后,比如JavaScript改变样式或添加节点,这时候render树就需要重新计算。
7、重绘repaint: 重新绘制页面。
HTML整个解析过程看起来很简单,但是我们要知道解析过程中css、js和dom的加载顺序。我们都知道HTML是自上往下解析的,在解析过程中:
1、如果遇到link和style,那就就会去下载这些外部的css资源,但是css跟DOM的构建是并行的,就是说不会阻塞DOM树的构建。
2、如果遇到script,那么页面就会停止html的解析和渲染把控制权交给JavaScript,直到脚本加载完毕或者是执行完毕。
1> 没有defer和async标签的script会立即加载并执行。 2> 有async标签的js,js的加载执行和html的解析和渲染并行。 3> 有defer标签的js,js的加载和html的解析和渲染并行,但会在html解析完成后执行,在触发DOMContentLoaded事件前执行。
3、页面的渲染是依靠render树,也就是说如果css没有加载完成,页面也不会渲染显示。
4、JavaScript执行过程中有可能需要改变样式,所以css加载也会阻塞JavaScript的加载。
5、JavaScript执行过程中如果操作DOM,但是DOM树又是在JavaScript之后才能构建,就会报错,找不到节点。
6、DOMContentLoaded和onload的区别:DOMContentLoaded在html解析完毕后执行,loload在页面完全加载完成后执行(包括样式和图片)。
html相关优化
其中对我们项目首屏启动速度影响最大的就是网络请求,所以优化的重点就是使用文件缓存和减少Http请求(页面中每发送一次请求,都会完成请求+响应这个完成的HTTP事务,会消耗性能,造成HTTP链接通道阻塞)。
1.html代码压缩。
2.减少页面上引用的文件数量(非首屏依赖CSS或者js文件、图片资源的引用,等首屏展示后静默下载(按实际项目需求))。
3.减少域名查询:DNS
查询和解析域名也需要消耗时间,不同域名使用越少越好。
4.优化页面加载顺序:1.将css
文件放在head
中 2.js
放在body
的底部。
以下贴出示例的html文件内容,以注释进一步说明优化内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>title</title> <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1, minimum-scale=1,maximum-scale=1" /> <meta name="full-screen" content="yes" /> <meta name="x5-fullscreen" content="true" /> <meta name="360-fullscreen" content="true" /> <meta name="screen-orientation" content="portrait" /> <meta name="x5-orientation" content="portrait"> //初始化只加载首屏页面渲染依赖的css文件,非初始化依赖css文件,等游戏主包js下载后初始化由WebCssManager.js动态引入 <link rel="stylesheet" href="./css/layout.css"> <script> function load() { //删除定制页 } </script> </head> <body onload="load()"> <script src="src/res/loading.js"></script> //游戏定制页,在主包下载之前过度显示,避免因下载主包导致首屏展示太慢,提高游戏体验 <script> //SDK或者外部插件,全部由插件管理类管理,初始化依赖的SDK在管理类下载完后立即执行,其他插件或者渠道SDK等主包下载完成后下载 var loadPluginManager = function() { var script = document.createElement('script'); script.onload = function() {//下载js主包} script.onerror = function(err) {loadPluginManager();} script.src = "./plugin/webPluginManager.js"; document.body.appendChild(script); } loadPluginManager(); </script> <canvas id="gameCanvas" width="1136" height="640"></canvas></body> </html>//html代码逻辑尽量少,只引入少量文件避免过多的HTTP请求
HTTP相关优化
1.如果支持http合并请求就合并请求。合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。 静态资源打包,因为浏览器下载静态文件的时候是有线程数限制的:同一时间针对同一域名下的请求有一定数量限制,超过限制数目的请求会被阻塞。为了提高性能,服务器端会把js/css 合并成一个文件(因为都是文本嘛)再向客户端输出,这样页面能更快的展现。 2.缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。
CSS使用注意
1.正确使用 Display 属性,因为 Display 属性会影响页面的渲染。 2.避免图片和 iFrame 等空 Src。 3.尽量避免重设图片大小。 4.避免 CSS 表达式。 5.移除空的 CSS 规则。 6.不滥用 Web 字体、Float。 7.不声明过多的 Font-Size。 8.值为 0 时不需要单位。
游戏流程优化
示例:打包出来的项目结构(不含子包),其中主包js文件大小2.9MB左右,资源文件大概15MB(压缩后,png类型资源在打包的时候会额外生成一份Webp格式(android使用webp、ios使用png))。
项目结构优化:
1.(棋牌大厅)游戏主包或者游戏子包(棋牌游戏)的发布直接覆盖原包内容(便于没变动文件使用缓存),其中主包目录和子包目录独立分开,便于版本缓存管理。
资源相关优化:
1.打包并压缩所有游戏js文件。
2.合并所有CSS文件(按自身项目情况而定)。
3.合并非初始化所需插件或者SDK文件(按自身项目情况而定)。
4.压缩图片资源,png纹理生成一份webp格式(并再次压缩75%)供android使用(按自身项目情况而定,因为webp纹理下载后需要解码,算上解码耗时小图使用webp不划算)。
5.合并所有资源集合图plist文件(减少大量http请求数、IO)。
6.项目结构中大厅游戏版本号只影响html文件和主包文件名,资源文件依赖MD5,游戏子包根据服务器下发版本号区分http请求。
逻辑相关优化:
前言:因为我们游戏APP、微信小程序、h5都是共同游戏服务器,针对h5通讯流程后端没做任何优化,共用一套(连接大厅服务器(含请求进入大厅相关接口)、连接游戏服务 器(webSocket)(含请求进入游戏房间相关接口)耗时长),充分利用通讯耗时做该做的事情(如下载图片资源):异步执行游戏中同步顺序执行的逻辑(按实际情况而定)。
1.连接大厅服务器、请求大厅相关接口的同时下载游戏资源(部分资源直接载入纹理缓存)(此处必须保持异步平衡,连接大厅耗时充分用来下载和预览资源)(注意控制同时请求数量,因为大部分浏览器并发请求数是4-6个)。
2.进游戏房间同上。
3.异步执行影响游戏流程中部分独立不互相影响的流程(如GPS、影响游戏流程的接口)。
4.通过分享进游戏,只预览少量大厅通用资源和对应游戏资源,跳过大厅相关流程且不创建大厅相关界面,直接进游戏房间(通讯和游戏资源下载预览同上,且保持异步平衡)。
游戏渲染优化
1.尽可能让相同图集里的图一次性连续渲染完。2.减少被遮挡或者超出可视区的单位渲染。3.部分UI元素较多的界面或者场景建议使用分帧加载,元素量大的则规划渲染规则。4.减少算法复杂度。5.合并资源减少IO。6.允许情况下减低帧率。
代码编写优化
这里先推荐一本书:《代码整洁之道》:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。
js原生库推荐 : Lodash (一个一致性、模块化、高性能的 JavaScript 实用工具库。它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数)。
1.使用函数式编程
2.编写代码的时候,更多的使用异步编程
3.在js中尽量减少闭包的使用
4.封装过程中,尽量做到低耦合高内聚。
来源:https://www.cnblogs.com/xyptechnology/p/11996591.html