script 标签 defer 与 async

JS 加载.png

普通 script 标签

  • JS 可能会修改 HTML 和 CSS,所以 JS 的下载执行过程不能和 HTML / CSS 并行
  • HTML 解析过程当中若碰到外联的 JS,会暂时停止 HTML 的解析流程,等候脚本下载和解析完成后再继续进行以前中断掉的 HTML 解析流程
  • 这样就致使了 script 标签外联 JS 加载有这样的缺点:会影响整个页面效率,一旦网速很差整个网站将等待 JS 加载而不进行后续渲染,因为中断了 HTML 解析流程,因此致使页面空白,影响体验
  • 之前的写法是将 script 标签写在 body 最后面,等 DOM 所有解析完成后才加载 JS,HTML5 标准有另外一套异步加载 JS 的方法

defer

  • 在 script 标签的行间写一个 defer=“defer” 或直接写 defer 就可让这个 script 外联的 JS 变成异步加载了
  • HTML 解析流程中若碰到外联 JS,会开辟新线程来下载脚本,下载完成后不会当即解析,不会阻塞 HTML 的解析流,等到 HTML 解析完毕后(不包括下载完里面的资源),再进行脚本的执行解析
  • 该方法只有 IE 和一些高版本的 firefox 和 chrome 能够用
  • 这种方式能够在 script 标签里面写代码
  • 注意:IE6 和 IE7 的异步加载最多只能有 2 个,超过两个时必须等前两个加载完才会加载第三个
  • 全部 defer 的 JS 代码都保证按照顺序执行

async

  • async 是 asynchronous 的缩写,是 HTML5 标准,
  • HTML 解析流程中若碰到外联 JS,会开辟新线程来下载脚本,下载完成后当即解析执行,且解析流程会中断 HTML 解析流程,等到脚本执行完成后才会继续进行以前中断掉的 HTML 解析流程
  • 这种方法除了 IE6 ~ IE8 其余的浏览器都好用
  • 该方式不能把代码写在 script 标签里,只能引用外部脚本(虽然标准是这么写的,但如今随着内核升级 async 的 script 标签里也能够写代码,在没有 src 状况下)
  • async 的 JS 代码不能保证是顺序执行的,按照 race 的方式哪一个脚本先下载完就先解析哪一个脚本
  • defer 和 async 这两个属性不能一块儿使用

兼容性写法

  • 直接写两个 script 标签,一个是 defer 一个是 async,这种方法有缺陷,IE 高版本会加载两遍引发冲突,有些浏览器两个都没有,会一个都加载不出来
  • 经过动态添加 script 标签,W3C 的标准规定动态添加的 script 标签是异步的
  • 这里 src 部分的下载是异步的,不会阻塞后面的代码执行,便可一边把 script 插入到 DOM 中一边下载资源
// 须要注意的是 readyState 的 if-else 必定要写在 script.src = url 和 appendChild 以前
// 因电脑速度可能会很快,刚走到 src = url 部分就已经加载完毕了, 此时 readyState 已变成 loaded,后面就不会触发 onreadystatechange 事件
// 若回调函数是写在须要加载进来的文件里的方法,须要把该方法放到匿名函数里,这样在语法解析时才不会由于函数未声明而报错

<script>
    function loadScript(url, callback) {
        const script = document.createElement("script");
        script.type = "text/javascript";
        if (script.readyState) { // IE 和高版本的 chrome、firefox
            script.onreadystatechange = function() {
                if (script.readeyState === "loaded" || script.readeyState === "complete") {
                    script.onreadystatechange = null;
                    callback && callback();
                }
            }
        } else {
            script.onload = function() { // safari chrome opera firefox
                callback && callback();
            }
        }
        script.src = url;
        document.body.appendChild(script);
    }
</script>
复制代码
相关文章
相关标签/搜索