X5内核,解决系统webview兼容性差、加载速度慢、功能缺陷等问题
解决一切令开发者们头疼的问题,让开发者快速而轻松地开启开发之旅
在前面整理的Chrome官方的渲染性能优化文章中,讲述到了网页生成过程中,主要包含如下几个步骤:
JavaScript。一般来说,我们会使用JavaScript来实现一些视觉变化的效果。比如用jQuery的animate函数做一个动画、对一个数据集进行排序、或者往页面里添加一些DOM元素等。当然,除了JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations, Transitions和Web Animation API。
计算样式。这个过程是根据CSS选择器,比如.headline或.nav > .nav_item,对每个DOM元素匹配对应的CSS样式。这一步结束之后,就确定了每个DOM元素上该应用什么CSS样式规则。
布局。上一步确定了每个DOM元素的样式规则,这一步就是具体计算每个DOM元素最终在屏幕上显示的大小和位置。web页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如,元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。
绘制。绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的。
渲染层合并。由上一步可知,对页面中DOM元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
参考tikizheng在Timeline的入门篇中所整理的框图,更清晰地展示了页面生成的流程。
网页中的重绘过程是影响整体性能下降的关键点之一,因而网站开发者应该更多地去避免在站点中进行不必要以及不适时的重绘步骤,借助Inspector中的Timeline面板可以很好地剖析这一些存在的问题。
在此,本文将通过一个简单的网页Demo来讲述Timeline的常用技巧
Demo页面,具体如下:
1 <html> 2 <head> 3 <meta name="viewport" content="width=device-width,initial-scale=1"> 4 <title>X5 CORE</title> 5 <style type="text/css"> 6 body { font-size: 16px } 7 p { font-weight: bold } 8 span { color: red } 9 p span { display: none } 10 img { width: 100%; max-width: 300px } 11 </style> 12 13 <script> 14 function a_click() { 15 var span = document.getElementsByTagName('span')[0]; 16 span.textContent = 'Web performance'; // change DOM text content, lead to relayout 17 span.style.color = 'green'; // change CSSOM property 18 } 19 </script> 20 </head> 21 22 <body> 23 <p>Hello <span>web performance</span> students!</p> 24 <div><img src="X5.jpg" onclick="a_click()"></div> 25 <script> 26 var span = document.getElementsByTagName('span')[0]; 27 span.textContent = 'interactive'; // change DOM text content 28 span.style.display = 'inline'; // change CSSOM property 29 // create a new element, style it, and append it to the DOM 30 var div = document.createElement('div'); 31 div.textContent = '*** Add new element to DOM ***'; 32 div.style.color = 'blue'; 33 document.body.appendChild(div); 34 </script> 35 </body> 36 </html>
这是一个很简单的网页,展示了一行文字和图片,而在body中有一段script对个别元素进行样式和内容的调整;此外还有一个点击事件,即点击图片后,会再次执行一段修改元素内容和样式的脚本。
Ctrl+E 开始录制
刷新页面
点击图片,执行onclick事件
Ctrl+E 结束录制
操作完毕后,Inspector在Timeline中记录了这一过程中,与页面相关的各项信息。
可以看到下图中上方的两个红色框位置,该区域是Timeline面板的整体预览区,分了三部分(FPS、CPU、NET)来展示,具体可查看Timeline使用详情。首个红色框位置,记录了首次加载页面时,所经历的Loading -> Scripting -> Rendering -> Painting流程。而右边的红色框区域中,可见CPU中首先显示了黄色(代表Scripting)的峰形区域,随后显示了紫色(代表Rendering)的峰形区域,表示了页面在响应点击事件后所进行的流程。
通过滚轮在Flame框图中,可以对页面中的事件进行缩放,可已清晰地观察到在首次加载过程中,所经历的Loading -> Script -> Layout -> Paint -> Composite Layers等流程。
并且,在点击了各个事件之后,下方的Summary中会罗列出更加具体的信息。比如,点击Evaluate Script事件后,可以查看总共的耗时,并且可以链接到具体的JS源代码:
而在网页加载完毕后,对图片进行了点击操作,触发了 img 标签的onclick事件,开发者能够在Flame框图中查看到点击事件中各个流程,其展现了所有的JS调用栈: 系统Event(click) ==> 绑定的onclick事件(html中第24行) ==> function a_click() (html中第14行):
由此可见,当在页面已经得到生成了之后,利用JS去更改个别元素的内容(DOM结构变化),或者是调整元素属性(CSSOM属性变化),都会引起页面重新进行Rendering -> Painting流程。如果这些是不必要的操作,则必定会导致网页性能降低。
因此,对于开发者来说,应该要知道如何去定位网页中发生重绘的区域。
开启方式:在控制栏的右上角属性按钮中,选择More tools -- Rendering settings,然后在弹出的面板中选择 Paint Flashing。
如下图中的操作,在勾选了Paint Flashing后,还是在Demo页面中,点击图片触发JS事件,进而会span标签的内容以及颜色,而在页面预览区域中,可以观察到该行文本在刷新内容过程中,有绿色的方框进行高亮包围,说明了这一部分区域发生了重新绘制。 另外一个重要的现象是,虽然点击后的JS事件仅修改了 span的内容,但是重绘却发生在整一个p标签中,说明了个别元素的重新绘制,一般会影响到父元素或者是周围的元素,造成区域性重绘,因此在页面中避免不必要的重绘显得至关重要。
小技巧: 当发现页面中,如果存在一些不必要的重绘现象,而又不能够定位到具体的原因,可以对该区域中的各个元素,依次进行隐藏(在Element面板中设置visibility:hidden), 观察效果来定位。
在More tools -- Rendering settings中,还可以开启 Layer Borders,观察页面中的各个区域绘制情况。通过这一项功能,开发者能够发现页面中发生动画或者是CSS transforms/transitions等发生了形状或位置变化的元素,进而优化其渲染时间。
Demo页面比较简单所以效果不明显,利用官方图片来展示Layer Borders的效果:
当在Flame框图中点击了 一个Paint事件,则会在详情面板中出现一个Paint专有标签:Paint Profiler
通过Paint Profiler面板,开发者可以知道该次Paint事件的绘制时间、绘制位置和大小等信息,并且能够具体到某一个元素的绘制耗时:当拖动标尺,直至内容框中仅有目标元素Image的绘制时,即可观察到其耗时(0.14ms/0.2ms),以及图片区域的大小、位置等等信息。