您好,欢迎来到二三娱乐。
搜索
您的当前位置:首页无阻塞脚本

无阻塞脚本

来源:二三娱乐

什么是阻塞?

我的理解:当外部引入的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);
    }

     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) 来得知页面是否已经准备好了。
对于这种方式,有完整的开源实现,只需要引用就行了:

  • 雅虎的

更多参考

Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务