公司H5游戏客户端性能优化整理

MaureenChen 2019-12-09

近期在一家公司负责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">

</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.相同图集里的图一次性连续渲染完,减少Draw Call。2.减少被遮挡或者超出可视区的单位渲染。3.部分UI元素较多的界面或者场景建议使用分帧加载,元素量大的则规划渲染规则。4.减少算法复杂度。5.合并资源减少IO。6.允许情况下减低帧率。

代码编写优化

这里先推荐一本书:《代码整洁之道》:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。

js原生库推荐 : Lodash (一个一致性、模块化、高性能的 JavaScript 实用工具库。它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数)。

 1.适当使用函数式编程

 2.编写代码适当的使用异步编程

 3.在js中尽量减少闭包的使用

 4.封装尽量做到低耦合高内聚。

相关推荐