DOM渲染机制与常见性能优化

参考资料

零、前言

本人垃圾,毁人无数。之前在 JavaScript客户端开篇 一文提及JavaScript客户端的时间线,本篇将做进一步详细的叙述。主要包括渲染的路径,以及渲染性能,并介绍一些常见的渲染优化方法,及其他前端相关优化要点。

一、主要渲染原理

1.0 前情提要

时间线

1.1 构建对象模型

浏览器处理过程
  • 转换:浏览器从磁盘或网络读取 HTML 的原始字节,然后根据指定的文件编码格式(例如 UTF-8 )将其转换为相应字符
  • 符号化:浏览器将字符串转换为 W3C HTML5 标准 指定的各种符号
  • 词法分析:发射的符号转换为 对象 ,定义它们的属性与规则
  • DOM 构建:因为 HTML 标记定义不同标签间的相互关系(某些标签嵌套在其他标签中),所以创建的对象在树状数据结构中互相链接,树状数据结构还捕获原始标记中定义的父子关系:比如 HTML 对象是 body 对象的父对象, body 是 paragraph 对象的父对象等等
CSS 对象模型
  • CSSOM 采用树状结构的原因:在给页面上的一切对象计算最终的样式集时,浏览器会先从应用给该节点的最通用规则开始(例如,如果节点是 body 元素的子元素,则应用所有 body 样式),然后,通过应用更具体的规则递归细化计算的样式,即 规则向下层叠
  • 上面的树不是完整的 CSSOM 树,它只是显示了我们决定在样式表中覆盖的样式。每个浏览器都会提供一套默认的样式,也称为 用户代理样式,即我们不提供任何自定义样式时看到的样式
  • CSS 规则转换的过程跟 HTML 相似,如下图:
CSSOM 转换过程

1.2 渲染树构建、布局和绘制

Render Tree
  • DOM 树与 CSSOM 树融合成渲染树,其中渲染树只包括渲染页面需要的节点(不包括 <script><style>display: none; 等)
  • 布局(也称为重排reflows))计算每个对象的精确位置及尺寸
  • 最后一步的绘制,输入确定的渲染树,在屏幕上渲染像素

1.3 CSS/JS阻塞

  • CSS相关:
  • 默认情况下,CSS 被视为阻塞渲染的资源
  • 媒体类型与媒体查询允许我们将一些 CSS 资源标记为不阻塞渲染
  • 所有 CSS 资源,无论阻塞或不阻塞,浏览器都会下载。
  • JavaScript相关:
  • JavaScript 可以查询、修改 DOM 与 CSSOM
  • JavaScript 的执行因 CSSOM 而阻塞
  • 除非明确声明 DOM 构建为异步,否则 JavaScript 会阻塞这一流程
  • 实例分析 CSS 和 JavaScript 默认与异步情况下浏览器的执行流程
// demo 1
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div>![](awesome-photo.jpg)</div>
    <script src="app.js"></script>
  </body>
</html>
demo 1 执行流程
// demo 2
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div>![](awesome-photo.jpg)</div>
    <script src="app.js" async></script>
  </body>
</html>
demo 2 执行流程
// demo 3
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet" media="print">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div>![](awesome-photo.jpg)</div>
    <script src="app.js" async></script>
  </body>
</html>
demo 3 执行流程

1.4 通过异步编程减少关键路径阻塞

  • 通过以上的不同执行路径分析,可以得出以下通过异步编程减少关键路径阻塞的结论:
  • 首屏的 DOM 结构尽量简单
  • 若 JavaScript 代码不影响 DOM 构建,使用 async (放在 <body> 底部不一定不会影响最初的 DOM 构建,主要看具体浏览器的执行机制)
  • 若 HTML 语义化较好,CSS 文件可使用媒体类型 media= "print" 使其不阻塞渲染

1.5 前端自动化打包的优化方式

  • 为减少 http 请求次数,现在的自动化打包工具都会把 JavaScript 文件打包到不超过 4 个 JavaScript 文件中(浏览器同时请求上限为 4 个),因此加载速度会较慢,故 Web App 经常采用首屏页面加载
  • 首屏页面渲染完毕后,再加载 bundle.js ,加载完毕后进行页面的重排(reflows)和重绘(repaint),进入单页面应用(Single Page Application)

二、性能优化

2.0 前情提要

  • 本节性能优化内容基于第一节的浏览器渲染原理,根据各个流程给予一定的性能优化指南。内容跟第一节有部分重复。

2.1 加载优化(最耗时)

  • 减少 HTTP 请求:浏览器一般同时响应请求为4个请求(PC 一般为4个,Android 支持4个,IOS 5后可支持6个),所以尽量减少页面的请求数,首次加载同时请求数不能超过4个。(Webpack打包等)
  • 合并 CSS、 JavaScript;
  • 合并小图片、 使用 CSS sprite,base-64;
  • 缓存:使用缓存可以减少向服务器的请求数,节省加载时间,所以所有静态资源都要在服务器端设置缓存,并且尽量使用长 Cache (长 Cache 资源的更新可使用时间戳)
  • 缓存原理参考
  • 缓存一切可缓存的资源;
  • 使用长 Cache (使用时间戳更新 Cache);
  • 使用外联式引用 CSS、JavaScript(可使用 localstorage 缓存图片);
  • 压缩 HTML、CSS、JavaScript:减少资源大小可以加快网页显示速度,所以要对HTML、CSS、JavaScript 等进行代码压缩,并在服务端设置 GZip
  • 压缩(例如多余的空格、换行符和缩进),自动化工具或在线压缩工具;
  • 启用 GZip
  • 使用首屏加载:首屏的快速显示,可以大大提升用户对页面速度的感知,因此应尽量针对首屏的快速显示做优化;
  • 按需加载:将不影响首屏的资源和当前屏幕资源不用的资源放到用户需要时才加载,可以大大提升重要资源的显示速度和降低总体流量。但是这也会导致大量重绘,影响渲染性能
  • LazyLoad
  • 滚屏加载
  • 通过 Media Query 加载
  • 预加载:大型重资源页面(如游戏)可使用增加 Loading 的方法,资源加载完成后再显示页面。但 Loading 时间过长,会造成用户流失。
  • 对用户行为分析,可以在当前页加载下一页资源,提升速度
  • 图片压缩:图片是最占流量的资源,因此尽量避免使用它,使用时选择最合适的格式(实现需求的前提下,以大小判断),合适的大小。
  • 使用 智图 压缩;
  • 使用其他方式代替图片(CSS3,SVG,IconFont);
  • 使用 Srcset (主要移动端);
  • 选择合适的图片(webP优于JPG,PNG8优于GIF);
  • 选择合适的大小(首次加载不大于1014KB,不宽于640(基于手机的一般宽度));

2.2 脚本执行优化

  • CSS 写在头部(阻塞 DOM 渲染,不阻塞加载,内联会阻塞加载),JavaScript 写在尾部或异步(默认阻塞加载和渲染)
  • 避免图片和 iFrame 等的空 Src:空 Src会重新加载当前页面,影响速度和效率
  • 尽量避免重设图片大小:指通过 CSS、JavaScript 等中多次重置图片大小,多次重设图片大小会引发图片的多次重绘,影响性能
  • 图片尽量避免使用 DataURL ,前面提到 DataURL可以减少加载时间,但是 DataURL 没有使用图片的压缩算法文件会变大,并且要解码后再渲染,耗时长,综上应尽量避免

2.3 CSS优化

  • 尽量避免在 HTML 标签中写 Style 属性
  • 避免 CSS 表达式:CSS 表达式的执行需跳出 CSS 书的渲染,因此请避免 CSS 表达式
  • 移除空的 CSS 规则:空的 CSS 规则增加了 CSS 文件的大小,且影响 CSS 树的执行,所以需移除空的 CSS 规则
  • 使用 flexbox 代替传统的布局模型
  • 正确使用 display 属性:
  • display:inline 后边不应再使用 widthheightmarginpadding 以及 float
  • display:inline-block 后不应该使用 float
  • display:block 后不应该再使用 vertical-align
  • display:table 后不应该再使用 marginfloat
  • 不滥用 floatfloat 在渲染时的计算量比较大,尽量减少使用
  • 不滥用 Web 字体:Web字体需要下载,解析,重绘当前页面,尽量减少使用
  • 不声明过多的 font-size: 尽量使用语义化标签的默认字体大小,提高 CSS 树的效率
  • 值为 0 时不需要任何单位
  • 标准化各种浏览器前缀
  • 没前缀应放在最后
  • CSS 动画只用(-webkit- 无前缀 两种即可)
  • 其他前缀为 -webkit--moz--ms-无前缀 四种
  • 避免让选择器看起来像正则表达式:高级选择器执行耗时长且不易读懂,避免使用

2.4 JavaScript执行优化

  • 减少重绘和回流
  • 避免不必要的 DOM 操作;
  • 尽量改变 Class 而不是 Style ,使用 classList 代替 className ;
  • 避免使用 document.write()
  • 减少 drawImage
  • 缓存 DOM 选择与计算:每次 DOM 选择都要计算,用一个变量保存这个值;
  • 尽量使用事件代理,避免批量绑定事件;
  • 尽量使用 ID 选择器:ID选择器是最快的;
  • Touch 事件优化:使用 touchstarttouchend 代替 click,但注意 Touch 响应过快,易引发误操作;

2.5 渲染优化

  • HTML 使用 viewportviewport 可以加速页面的渲染;
  • 减少 DOM 节点:DOM 节点太多影响页面的渲染,应尽量减少 DOM 节点
  • 动画优化:
  • 尽量使用 CSS3 动画
  • 合理使用 requestAnimationFrame 动画代替 setTimeout (跑在主线程上,一般一秒刷新 60 次,提高动画帧的利用效率),参考文章 requestAnimationFrame & CSS3 animation
  • 适当使用 Canvas 动画, 5 个元素以内使用 CSS 动画(IOS8可使用webGL);
  • 高频事件优化:TouchmoveScroll 事件可导致多次渲染
  • 使用 requestAnimationFrame 监听帧变化,使得在正确的时间进行渲染;
  • 增加响应变化的时间间隔,减少重绘次数
  • GPU 加速:CSS中以下属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、webGL、Video)来触发 GPU 渲染,但过度使用会引发手机耗电增加。

END:初步整理,望各位留下评论共同探讨。不胜感激!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 大家都知道万维网的应用层使用了HTTP协议,并且用浏览器作为入口访问网络上的资源。用户在使用浏览器访问一个网站时需...
    SylvanasSun阅读 2,143评论 1 12
  • 围绕前端的性能多如牛毛,涉及到方方面面,以我我们将围绕PC浏览器和移动端浏览器的优化策略进行罗列注意,是罗列不是展...
    流动码文阅读 675评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,808评论 25 707
  • 她就这样等待着,漫漫地等待某一天。她的意中人会身披金甲战衣,脚踩七色的云彩来娶她。沉浸在幻想中的她是幸福的,拥抱着...
    文艺小贩阅读 200评论 0 0
  • 对心理学和精神科学一无所知的我,近日却被一部惊世骇俗之作《24个比利》所深深吸引。 这是一部由美国一位拥有心理学背...
    雨儿rain阅读 574评论 3 4