无阻塞脚本

什么是阻塞?

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容