从script标签的摆放位置来看看DOM树的渲染

其实这一篇的名字应该是 我眼中的页面渲染 才对,只是因为参考的那篇文章写得比较有趣所以改了改2333

参考文章:


说说js标签的位置

为什么大家普遍把<script src=""></script>这样的代码放在body最底部?

其实我之前看到这个问题的时候,脑海里和作者给的常规回答一样

因为浏览器生成Dom树的时候是一行一行读HTML代码的,script标签放在最后面就不会影响前面的页面的渲染。

其实乍一看没什么问题,但是这里需要明确页面渲染到底是什么概念。

不过比较好理解的是,上面这个答案指的是,因为页面渲染会因为的位置导致页面显示出现问题。

先说说页面渲染,再来继续位置的问题:


页面渲染的过程

  1. 首先浏览器请求html代码,然后并行发起image,css,js下载请求(无论在不在head里。这个随便找个图多的网站刷新看下timline就知道了),接着开始生成dom树

    • 注意是获取完html代码,读完之后才开始生成dom树

    • 根节点就是document对象,其他tag都被解析成dom树上的节点

    • DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。

    • 包括display:none的节点,因为这个时候样式还没有生效

    • 包括js动态生成的节点

  2. 然后读取css样式,生成CSSOM)

    • 浏览器会过滤掉不认识的前缀,比如opera会优先读取-o-的,别的都什么-mz--ms-啊忽略掉
  3. 生成render tree,其实就是把html代码和css样式结合起来,这部分就是即将实实在在显示出来的部分

  4. 接着浏览器就开始计算节点位置,绘制页面

    • head部分不会被渲染出来,因为不需要显示

    • display:none的节点就不显示了,理由同上

关于重绘和回流

  • render tree构建完毕,浏览器就开始绘制页面,这种包含对结构进行调整的就叫回流,至少会有一次
    (比如对元素的布局、尺寸的调整等)

  • 重绘主要是表面上的,即回流结束后重新绘制受影响而调整的部分,比如换个颜色啥的,不会对结构造成影响

  • 回流引起重绘,但是重绘不一定导致回流,因为根据js对样式或者dom的操作,重新绘制页面不一定会改变结构

回流何时发生
  1. 页面渲染初始化
  2. 添加或删除可见的dom元素
  3. 元素位置发生变化
  4. 元素内容发生变化(如文本变化或者图像变化引起的元素大小的变化)
  5. 改变了盒子的尺寸(border, padding, margin, width, height)
  6. 浏览器窗口尺寸发生改变
如何减少重绘和回流
  1. 直接修改className,或者使用cssText
    浏览器优化:浏览器会把多次会引发重绘和回流的操作放进一个队列里
    但是如果没有这个优化的话,每次操作都是立刻执行就会影响render tree,一次性执行多条语句效果就会好很多

  2. 尽量不要多次访问会引起浏览器flush队列的属性

    1. offsetTop, offsetLeft, offsetWidth, offsetHeight

    2. scrollTop/Left/Width/Height

    3. clientTop/Left/Width/Height

    4. width,height

    5. 请求了getComputedStyle(), 或者 IE的 currentStyle

      1
      2
      3
      4
      5
      6
      var computed;
      if(document.body.currentStyle) {
      computer = document.body.currentStyle;
      }else{
      computer = document.defaultView.getComputedStyle(document.body, '');
      }
  3. 利用缓存访问会引起浏览器flush队列的属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 别这样写,大哥
    for(循环) {
    el.style.left = el.offsetLeft + 5 + "px";
    el.style.top = el.offsetTop + 5 + "px";
    }
    // 这样写好点
    var left = el.offsetLeft,
    top = el.offsetTop,
    s = el.style;
    for (循环) {
    left += 10;
    top += 10;
    s.left = left + "px";
    s.top = top + "px";
    }
  4. 减少回流规模
    eg.操作body,在body前新增一个节点会导致整个render tree回流,而在body后面增加则不会导致前面的节点回流

  5. 让要操作的元素进行”离线处理”,处理完后一起更新

    a) 使用DocumentFragment进行缓存操作,引发一次回流和重绘;
    b) 使用display:none技术,只引发两次回流和重绘;
    c) 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘


根据浏览器的渲染过程可以发现,因为dom节点需要一个节点全部生成完之后才能进行下一个节点的生成,同时js请求是和css,images同时发生的,所以不管在哪里,都会被加载,但是因为有的节点没有被构建出来,这个时候加载js会阻碍后面的dom节点的构建,所以页面上的显示就会中断,或者是显示效果会出问题。

因此应当尽量把放在body的后面

不过第一篇文章的原作者还提出了很有意思的东西:

比如js位置会影不会影响页面的加载速度——废话,不会的,但是有可能导致显示效果出问题,也就是影响渲染效果

然后,居然是篇常识……白瞎了……