原生JS基础知识(十七)

网页的渲染方式概览

  • render树的绘制

    • DOM树的绘制

      • 深度优先原则

      • 异步加载方式 : 比如<img src='source'/>, 元素节点的解析和图片资源的下载是异步的

    • css树的绘制

      • 深度优先原则
  • js渲染引擎按照render树的规则绘制页面

    • dom操作会导致dom树的重排( reflow )或css树的部分重绘( repaint )

    • reflow浪费效率较多, repaint浪费效率较少


异步加载JS

  • 由于js可以操作dom, 故js代码的执行会阻断页面的渲染

    • 但当加载过多js时一旦网速不好, 整个网站将等待js加载而不进行后续渲染等工作( 比如过长白屏时间 )

    • 有些js我们还希望用到才加载( 按需加载 )

    • 况且在加载一些工具js时也没必要阻塞文档

  • 所以产生了异步加载js的需求

  • 异步加载js的三种方案

    • 只有IE能用 : 使用defer属性让script标签异步加载js代码, 其中加载的js代码要等到dom文档全部解析完才会被执行( 不阻塞页面 ), 可将代码写到script标签内部
      // 方式一
      <script src="tools.js" defer="defer"></script>
    
      // 方式二
      <script defer="defer">
        var demo = 123;
      </script>
    
    • W3C标准方法 : 使用async属性让script标签异步加载js代码, 其中加载的js代码加载完就会被执行( 不阻塞页面 ), 不能将代码写到script标签内部

    • 常用兼容性好且实现了按需加载的方法 : , 加载完毕后callback

      // 创建script标签
      var script = document.createElement('script');
      script.type = "text/javascript";
      // 插入到dom中, 使js可被执行
      document.head.appendChild(script);
      // 绑定监听tools.js下载进度的事件
      if (script.readyState) {
        script.onreadystatechange = function () {
          if (script.readyState === 'complete' || script.readyState === 'loaded') {
            // IE版本 : tools.js下载完毕
          }
        }
      } else {
        script.onload = function () {
          // 非IE : tools.js下载完毕
        }
      }
      // 开始异步下载tools.js
      script.src = "tools.js";
    

JS加载的时间线

  • 创建Document对象, 开始解析web页面, document.readyState = 'loading'

  • 遇到link外部css, 创建新的线程加载, 并继续解析文档

  • 遇到script外部js, 并且没有设置async/defer, 浏览器加载js并阻塞页面, 等待js加载完成并执行该脚本, 然后继续解析文档

  • 遇到script外部js, 并且设置async/defer, 创建新的进程异步加载js, 主线程继续解析文档

    • 注意script标签也算作DOM节点

    • 注意异步加载的js禁止使用document.write(), 因为该方法会清空之前的文档流, 示例如下

    // html
    <div style="width:100px;height:100px;background-color:red"></div>
    
    // js
    window.onload = function () {
      document.write('a');
    }
    
  • 遇到img等, 先正常解析dom结构, 然后浏览器开启新线程异步加载src, 并继续解析文档

  • 当文档解析完成后( 生成DOM树 ), document.readyState = 'interactive';

  • document.readyState === ‘interactive’, 所有设置有defer的脚本会按照顺序执行

  • document对象触发DOMContentLoaded事件( 只能用addEventListener监听该事件 ), 这也标志着程序执行从同步脚本执行阶段转化为事件驱动阶段

  • 当所有的async的脚本加载完并执行后, img等加载完后, document.readyState = 'complete', window对象触发load事件

  • 从此, 以异步响应方式处理用户输入, 网络事件等

  // 时间线三部曲验证
  <script>
    console.log(document.readyState);
    addEventListener('DOMContentLoaded', function(){
      console.log(document.readyState);
    }, false);
    document.onreadystatechange = function () {
      console.log(document.readyState);
    }
  </script>

  • jQuery保证页面解析完毕之后再操作dom的写法如下

    • 原理就是基于document.readyState === 'interactive’状态和DOMContentLoaded事件
      // jQuery.js
      $(document).ready(function(){
        ...    
      });
    
发布了49 篇原创文章 · 获赞 29 · 访问量 1879

猜你喜欢

转载自blog.csdn.net/Brannua/article/details/105012563