浏览器的渲染过程

64 阅读4分钟

浏览器的渲染过程

第一步、获取html代码

从本地服务器或者请求服务器拿到HTML的代码,这时的代码是字节形式的,要进行多次转换最后成为Dom树,Dom树的特点就是可以部分解析这里的工作就到此结束

注:(没有defer和async的情况下)如果html解析时,碰到js代码会停止解析,会先把js代码执行完成后,才会继续解析html代码,可这是就出现了一个问题,如果这时css代码并没有执行完成,这就导致,无法达到渲染树阶段,所以这时,js只能等待css代码执行完成,然后再跟部分解析完的html一起渲染合成,生成渲染树,再进行布局,绘制,最后显示完成后,执行js代码,当js代码执行完成后,再回头去执行html剩下的代码、再重新进行合并、渲染、布局、绘制、显示

13dd97fde3eb94f0807d45708e5b47bd.jpeg

第二步、获取css代码

从本地服务器或者请求服务器拿到css的代码,同转换dom的流程一样,开始解析,最后转换成CSSOM(css样式表) 而CSSOM的特点就是一定要全部解析完成,才可以进入后续的合成render渲染树

注:CSSOM不会影响html的解析,他们是同时进行的,但是、因为合并的render渲染树是需要两者同时完成的,所以CSSOM会影响html的渲染

第三步、合成渲染树阶段

把解析完成的dom树和cssom合并渲染,生成我们所需要的render(渲染树),并对树中的元素位置,尺寸进行计算,并完成布局

第四步、绘制

绘制页面的像素信息,把渲染出来的每个图层传给GPU(GPU Process 专门用来做3D渲染的进程),进行3D渲染,最后生成页面

重绘和回流

重绘:当元素的背景颜色,边框颜色发生改变时,该元素会进行重绘

回流:当元素的几何属性(宽,高,内外边距)发生改变时,html渲染树会回流,重新渲染元素的结构样式

总结:回流一定会触发重绘,但是重绘不一定会触发回流、频繁的重绘和回流 尤其是回流会导致页面变慢,所以要尽量减少页面重绘和回流

浏览器的优化

  • 浏览器本身自己的优化 UI 渲染是异步的,它会把所有要绘制放在一个队列中,等到了一定的时间间隔才会一次性批处理绘制

  • 我们可以做的优化:我们要减少重绘和回流就是要减少对渲染树的操作,则我们可以合并多次的 DOM 和样式的修改

  • 尽量不要使用一堆的行内样式操作,而是把要操作的全部写在类 clas 中,然后直接调用这个类名

  • 在操作之前先把这块 display:none;然后把所有的操作全部做完,然后再 display:block,这样只会有两次重绘和回流

  • 使用克隆元素的技术,实现复用

  • 在写动画的时候可以用脱离文档流,避免对文档其他地方有影响

  • 使用 DocumentFragment 文档碎片(不要直接在 DOM 树上操作,而是先搞一个文档碎片,在这个文档碎片上把所有的操作全部做完,在这个文档碎片上做的任何操作不会触发重绘和回流,因为和 DOM 树没有关系,等操作全部搞定,再把这个文档碎片追加到 DOM 树上,只会导致一次重绘和回流)

<script>
// 没有使用文档碎片之前的写法,会导致101次重绘和回流
  var ul = document.createElement('ul')
  document.body.append(ul)
  for(var i=0;i<100;i++){
    var li = document.createElement('li')
    li.innerHTML = i;
    ul.append(li)
  }
</script>

**使用代码碎片时**

<script>
// 只有一次重绘和回流
    //documentFragment :文档碎片  把需要更新的元素加入到代码碎片中,当文档碎片,插入dom的时候统一更新,只会触发一次重绘和回流
      var documentFragment = document.createDocumentFragment();
      var ul = document.createElement("ul");
      documentFragment.append(ul);
      for (var i = 0; i < 100; i++) {
        var li = document.createElement("li");
        li.innerHTML = i;
        ul.append(li);
      }
      document.body.append(documentFragment);
    </script>

js会影响html的解析

解决方法:

1、把script标签放在body标签后面,等浏览器解析完成了,再去执行js代码 2、defer 和 async

2078efeadbbfdd49f08fcfb5c4d7f2e8.jpeg

当没有defer 和async的时候

当js引擎解析html代码的时候,如果有js代码,就会等js代码解析并执行完成后,才会继续解析后续的html代码

当有defer的时候

当js引擎解析html代码的时候,如果有js代码,且js代码携带了defer属性,不会影响html解析代码,会跟html同时解析js代码,且等html解析完成后,再执行js代码

当有async的时候

当js引擎解析html代码的时候,如果有js代码,且js代码携带了async属性,不会影响html解析代码,但是一旦Js解析完成后,就会立即执行js代码,这时html的解析会停止,直到js解析完成,html才会继续解析

总结:当我们实际开发的时候,取defer方法,最利于浏览器的优化,且提升用户的体验,不会出现html解析一半就开始渲染而造成页面的卡顿、渲染错误等