美文网首页js
无阻塞脚本

无阻塞脚本

作者: camiler | 来源:发表于2017-05-11 16:24 被阅读14次

什么是阻塞?

我的理解:当外部引入的js文件或者css文件一直没有下载成功,导致页面DOM没有渲染出来时,就形成了页面阻塞。这显然对用户体验很差。

怎么才能不阻塞?

从阻塞的形成,我们就知道造成阻塞可能有以下原因:

  • js,css文件较多,较大。下载时间长。
  • js 文件在DOM文档结构之前,js一直在下载或执行中。

所以针对以上可能的原因,我们发现根本在于JS加载执行时间和DOM渲染时间的冲突。那么,只要保证JS加载执行在DOM加载后,是不是就能保证是无阻塞脚本了呢。从技术的角度讲,就是在window的load事件触发之后再下载文件。
对于CSS,如果CSS比较少,可以采取内联的方式放在HTML<style></style>里面,可以看到,webpack在打包时就是引入相关的CSS内联到页面中。
对于JS,有以下几种方式:

  1. <script>标签放在<body>里面,但是DOM结构之后,这样就能保证DOM加载完再加载JS文件,或者script标签里的JS代码。
  2. HTML4 script标签有一个defer属性,意思是延迟。jquery.min.js会异步加载,不影响DOM的加载,只是会等到DOM加载完成后再执行。
<script src="jquery.min.js" defer></script>

注意:HTML5新增了一个属性async,它和defer的区别在于,在异步加载JS之后会自动执行,执行的过程可能会阻塞DOM。

  1. 动态脚本插入
<script type="text/javascript">
    function loadScript(url, callback){
        var script = document.createElement('script');
        script.type = "text/javascript";
        if (script.readyState) { //IE
            script.onreadystatechange = function(){
                if (script.readyState === 'loaded' || script.readyState === "complete") {
                    script.onreadystatechange = null;
                    callback();
                }
            }
        }else{
            script.onload = function(){
                callback();
            }
        }

        script.src = url;
        document.getElementsByTagName('head')[0].appendChild(script);
    }

    loadScript("https://www.google-analytics.com/analytics.js", function(){
        //js逻辑
        alert('loaded');
    })
    </script>

IE 对这两个 readyState 值所表示的最终状态并不一致,有时<script>元素会得到“loaded”却从不出现“complete”,但另外 一些情况下出现“complete”而用不到“loaded”。最安全的办法就是在 readystatechange 事件中检查这两种状 态,并且当其中一种状态出现时,删除 readystatechange 事件句柄(保证事件不会被处理两次)
通过这种方法,无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。你甚至可以将这些代码放在 <head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。同时,这种方法可以跨域加载,所以比较常用。

  1. XMLHttpRequest动态脚本注入
<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.open('get', 'file.js', true);
    xhr.onreadystatechange = function(){
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
                var script = document.createElement('script');
                script.type = 'text/javascript';
                script.text = xhr.responseText;
                console.log(script.text);
                document.body.appendChild(script);
            }   
        }
    }
    xhr.send(null);

执行之后如下:

image.png

但这种无法跨域下载,所以运用较少。

建议的无阻塞模式

通过以上的分析,权衡利弊之后,应该是通过loadScript方式,会更好一些。将页面初始化所需的JS单独加载之后,再通过动态加载的方式加载其他不需要立即执行的JS代码。 例如:

<script type="text/javascript" src="loader.js"></script> //初始化页面时需要的JS代码
<script type="text/javascript">
  loadScript("the-rest.js", function(){ 
      Application.init();
  }); 
</script>

另外,再结合第一种将script放在</body>之前,DOM之后。这样当第二部分 JavaScript 文件完成下载,所有应用程序所必须的 DOM 已经创建好了,并做好被访问的准备,避免使用额外的事件处理(例如 window.onload) 来得知页面是否已经准备好了。
对于这种方式,有完整的开源实现,只需要引用就行了:

更多参考

defer和async的区别
script的defer和async
什么阻塞了DOM

相关文章

  • 异步- async 和 defer

    无 async 和 defer浏览器立即加载并执行指定脚本(读到即加载并执行),阻塞文档解析 async脚本的加载...

  • 无阻塞脚本

    什么是阻塞? 我的理解:当外部引入的js文件或者css文件一直没有下载成功,导致页面DOM没有渲染出来时,就形成了...

  • js性能优化

    无阻塞式加载脚本-延迟脚本 在script标签上添加defer(延迟脚本)或者async(异步脚本)属性。相同点:...

  • JavaScript性能优化

    1,web页面脚本阻塞:由于脚本会阻塞页面其他资源的下载,因此推荐将所有的 标签尽可能放到 标签的底部,可以尽量减...

  • js页面渲染

    CSS和JS在网页中的放置顺序是怎样的? 脚本会阻塞后面内容的呈现 脚本会阻塞其后组件的下载 对于图片和CSS, ...

  • async和defer的作用是什么?有什么区别

    async 和 defer 的作用 async:async 属性标注的脚本是异步脚本,即异步下载脚本时,不会阻塞文...

  • AJAX请求(原生js,jQuery)

    概念 ajax (Asynchronous JavaScript and XML) 异步:(不会阻塞js脚本的运行...

  • JS: 非阻塞脚本

    defer属性 HTLM4为 标签扩张了一个属性: defer.这个属性指明元素中所包含的脚本不打算修改DOM, ...

  • 关于defer与async的总结

    为了不阻塞页面文档的解析,script脚本有两个属性,用于控制脚本的下载和执行 相同点: 1、都只能用于外部脚本 ...

  • 高性能JavaScript---加载和执行

    脚本位置 由于脚本会阻塞页面其他资源的下载, 因此推荐将所有的< script >标签尽可能放到< body >标...

网友评论

    本文标题:无阻塞脚本

    本文链接:https://www.haomeiwen.com/subject/mclotxtx.html