天天看点

浏览器渲染页面常见问题

构建过程中可能会产生的阻塞

  • ​html​

    ​​的代码,是从上到下一行行执行的,也就是说如果​

    ​js​

    ​​代码写在​

    ​head​

    ​​头里,且没有用加在​

    ​window.onload​

    ​​方法里,那么他是无法读取到​

    ​body​

    ​​里的标签的。之所以加在​

    ​window.onload​

    ​​里可以执行,是因为,​

    ​window.onload​

    ​​里的函数会在​

    ​dom​

    ​树加载之后执行。
  • 在遇到​

    ​link​

    ​​标签后,会在​

    ​link​

    ​​加载(从服务器下载)完毕后,再执行后续代码。但与此同时,如果还有外部文件,则是同时加载(不阻塞后续外部文件​

    ​link​

    ​​、​

    ​script​

    ​加载)。但是外部文件内的代码不会执行,只会在代码解析到它的时候执行。
  • ​script​

    ​​标签会阻塞​

    ​html​

    ​​解析,因为​

    ​js​

    ​​可能会改变​

    ​dom​

    ​​和​

    ​css​

    ​​,因此浏览器会先解析script,避免浪费时间。 要想避免阻塞的话,可使用​

    ​defer​

    ​​和​

    ​async​

    ​。
  • 对于动态创建的​

    ​link​

    ​​标签不会阻塞其后动态创建的​

    ​script​

    ​​的加载与执行,不管​

    ​script​

    ​​标签是否具有​

    ​async​

    ​属性。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script>
        var start = +new Date
    </script>

</head>

<body>
    test
    <script>
        var link = document.createElement('link')
        link.href = 'http://udacity-crp.herokuapp.com/style.css?rtt=2'
        link.rel = 'stylesheet'
        document.head.appendChild(link)
    
        var script = document.createElement('script')
        script.src = 'http://udacity-crp.herokuapp.com/time.js?rtt=1&a'
        document.head.appendChild(script)
    </script>
    <div id="result"></div>
    <script>
        var end = +new Date
        document.getElementById('result').innerHTML = end - start
    </script>
</body>
</html>      
  • ​js​

    ​​添加​

    ​async​

    ​​属性之后,​

    ​script​

    ​​加载的外部文件成为了异步加载,这时相当于它于原本的​

    ​html​

    ​​解析过程同步进行。所以他不会被任何加载过程阻塞,只会在自己加载完成之后执行。但是,异步执行的影响就是,它如要读取​

    ​dom​

    ​​节点,很可能会失败,因为它的加载和​

    ​html​

    ​​解析过程没有了先后顺序。另外,如果它要输出动态的​

    ​dom​

    ​​节点,就无法保证节点的位置,因为它添加的节点,是在​

    ​html​

    ​已解析的节点下顺序添加的。
<script src="http://localhost:8080/test.js" async></script>      
  • ​js​

    ​​添加​

    ​defer​

    ​​属性之后,​

    ​script​

    ​​加载的外部文件成为了异步加载,执行是同步的。脚本加载不阻塞页面的解析,脚本在获取完后并不立即执行,而是等到​

    ​DOM​

    ​树加载完毕执行。
  • 浏览器渲染页面常见问题
  • 如下代码,加上​

    ​defer​

    ​​后会报错​

    ​Uncaught ReferenceError: $ is not defined​

    ​​。​

    ​DOM​

    ​​树渲染结束前​

    ​body​

    ​​里的​

    ​script​

    ​已经执行了
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script defer src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>

<body>
    <div id="d1">我是内容</div>
    <script >
        console.log($('#d1').html())
    </script>
</body>
</html>      
  • async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。
  • defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件
  • defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。

什么情况会引起重排/回流(reflow)

  • 添加或者删除可见的DOM元素;
  • 元素位置改变——display、float、position、overflow等等;
  • 元素尺寸改变——边距、填充、边框、宽度和高度
  • 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
  • 页面渲染初始化;
  • 浏览器窗口尺寸改变——resize事件发生时;

如何减少和避免重排

  • Reflow 的成本比 Repaint 的成本高得多的多。一个节点的 Reflow 很有可能导致子节点,甚至父节点以及兄弟节点的 Reflow 。
  • 样式集中改变
  • 尽量不要在布局信息改变时做查询(会导致渲染队列强制刷新)
  • 同一个DOM的多个属性改变可以写在一起(减少DOM访问,同时把强制渲染队列刷新的风险降为0)
  • 如果要批量添加DOM,可以先让元素脱离文档流,操作完后再带入文档流,这样只会触发一次重排(fragment元素的应用)
  • 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。

DOM是什么?

DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。

浏览器渲染页面常见问题

DOM的作用

  • DOM 将HTML文档呈现为带有元素、属性和文本的树结构(节点树)。
  • 它允许运行在浏览器中的代码访问文件中的节点并与之交互。节点可以被创建,移动或修改。事件监听器可以被添加到节点上并在给定事件发生时触发。

什么是DOM渲染?

DOM树的构建是文档加载完成开始的?

Render树是DOM树和CSSOM树构建完毕才开始构建的吗?

继续阅读