外链论坛

 找回密码
 立即注册
搜索
查看: 1|回复: 0

加载第三方JS的各样姿势

[复制链接]

2521

主题

4612

回帖

9998万

积分

论坛元老

Rank: 8Rank: 8

积分
99981045
发表于 4 小时前 | 显示全部楼层 |阅读模式

(点击上方公众号,可快速关注)

作者:zmmbreeze / @zhoumm 

zencode.in/22.加载第三方JS的各样姿势.html

如有好文案投稿,请点击 → 这儿认识详情

网页中加载JS文件是一个老问题了,已然被讨论了一遍又一遍,这儿不会再赘述各样经典的处理方法。JS文件能够经过源自来分为两个纬度:第1方JS和第三方JS。第1方JS是网页研发者自己运用的JS代码(内容研发者可控)。而第三方JS则是其他服务供给供给的(内容研发者不可控),她们将自己的服务包装成JS SDK供网页研发运用。这篇文案关注的第三方JS文件的加载。

从网站研发者的方向来看,第三方JS相比第1方JS有如下几个区别之处:

下载速度不可控

JS位置域名与网站域名区别

文件内容不可控

必定有强缓存(Cache-Control/Expires)

倘若你的网站上面有非常多第三方JS代码,那样“下载速度的不可控”特别有可能导

致你的网站会被拖慢。由于JS在执行的时候会影响到页面的DOM和样式等状况。浏览器在解析渲染HTML的时候,倘若解析到需要下载文件的script标签,那样会停止解析接下来的HTML,而后下载外链JS文件并执行。等JS执行完毕之后才会继续解析剩下的HTML。这便是所说的『HTML解析被阻止』。浏览器解析渲染页面的抽象流程图如下:

第三方JS代码并不受网站研发者的掌控特别有可能会显现加载时间长乃至加载失败的状况此时候就会引起全部页面的加载速度变慢。第三方JS代码越多这种危害越大。根据互联网守则:

网站加载速度越慢,用户流失越多

因此思虑怎样在有非常多第三方JS的状况下,保准她们不影响到网站自己的加载速度。咱们能够异步加载这些第三方JS代码。

异步加载

异步加载JS的办法非常多,最平常便是动态创建一个script标签,而后设置其src和async属性,再插进到页面中。这儿有个DEMO。实质操作的代码如下:

<script>

functionloadScript(url){

varscrs = document.getElementsByTagName(script);

varlast = scrs[scrs.length - 1];

varscr = document.createElement(script);

scr.src = url;

scr.async = true;

last.parentNode.insertBefore(scr,last);

}

loadScript(test.js);

</script>

PS:为了避免IE8以前版本的bug,并且保证script能插进DOM树,因此这儿直接document.body.append(src),而是调用了insertBefore办法

改成异步加载第三方JS代码之后,在JS的下载过程中浏览器会继续解析渲染HTML。流程图就变成为了如下:

由于loadScript的操作运用JS实现的,因此在JS下载之前会有一段执行JS代码的消耗。然则这段JS代码很简单,火速就会执行完毕。

除了动态创建script标签的办法,异步加载JS的办法还有非常多其他奇技淫巧,这儿陈列了一下:

先下载再执行 – 经过XMLHttpReqeust对象JSONP办法下载可执行的JS文件,而后运用eval()或者script标签执行JS。第三方JS文件通常区别域名的且JS内容不可控,因此办法就不适用了

iframe中加载JS – 将你的JS文件直接放到另一个页面的HTML中,而后将此页面URL位置做为iframe标签src属性。此办法需要增多一次页面请求,况且由于是在iframe内部执行了,第三方JS文件本身需要修改,故并不是很适用

先缓存再执行 – 利用JS文件的强缓存,先运用new Image().src = http://url.com/sample.js之类(Object对象)的办法下载JS文件。而后在真正需要解析执行JS的时候下载(有缓存,不必再次下载)和执行JS文件。此办法不仅适用于JS文件,一样能够用于CSS文件。这般咱们能够将静态文件的下载和解析执行(运用)掰开,批量并行下载,而后在合适的机会解析执行(运用)。但此办法需要强缓存的协同,第三方JS为了在版本发布时更早的更新JS代码通常都不会设置缓存,乃至有些第三方JS的代码是服务器端动态生成的。因此不是适用于第三方JS。

浏览器预加载机制

动态创建script标签的办法能够异步加载第三方JS,但它有缺陷。倘若加载代码之前有外链JS文件或CSS文件需要下载,如下面的代码:

<script src="app1.js"></script>

<script src="app2.js"></script>

<script>

functionloadScript(url){

varscrs = document.getElementsByTagName(script);

varlast = scrs[scrs.length - 1];

varscr = document.createElement(script);

scr.src = url;

scr.async = true;

last.parentNode.insertBefore(scr,last);

}

loadScript(test.js);

</script>

那样会先下载解析app1.js和app2.js再执行咱们的loadScript办法因此第三方脚本的下载会被暂停。流程图如下:

而如今咱们页面中代码如此繁杂,触发这种case的状况非常多。上面的DEMO中实质下载过程确实是这般,动态创建script标签方式下载的test.js需要等到其他CSS和JS文件下载执行完毕之后才起始下载。如下图:

虽然这对页面原有JS的执行不会有大的影响,但会影响到第三方JS代码本身的下载与执行。怎样处理这个问题呢?

你可能已然发掘上面的例子有个问题:HTML代码中g.js的位置在test.js之后却先下载了。其实这得益于浏览器的预解析机制,会先对HTML代码做静态分析找到外链的JS和CSS文件,而后并行下载下来(然则执行次序不变)。IE>=8 及其他主流浏览器基本都实现了这个功能。因此在这些支持预先载的浏览器中流程图应该是这般的:

为了利用预加载这个特性,咱们能够运用如下的写法:

<script src="app1.js"></script>

<script src="app2.js"></script>

<script src="./test.js"async></script>

运用标准的script标签写法,保证浏览器能够正确的识别这是一个外链JS文件。同期设置async标签,浏览器便会异步加载test.js文件,不会暂停掉浏览器的解析执行。流程图如下:

这儿有一个DEMO(http://zmmbreeze.github.io/blog/demo/load_script/async_script.html)。

但它并不完美,由于有些旧浏览器并不支持async属性。这会引起这个test.js文件在这些浏览器中不是异步的,并且会阻止掉页面渲染。有一个好信息是移动浏览器大多都支持async标签,倘若你的用户大都是移动浏览器的,你的制品不支持旧浏览器,那样能够运用这种办法

当然倘若你不介意第三方JS代码(本身支持支持)被延后到页面解析完毕后执行,那样能够再加上defer属性:

<script src="./test.js" async defer></script>

Firefox支持defer属性要比支持async早一点点。况且当浏览器同期运用了async和defer属性之后,浏览器会忽略defer属性。因此能够安心同期运用async和defer属性。针对不支持async的浏览器,会自动fallback到defer。不外支持程度就多了一点点,Firefox的旧版占比已然很低了,基本能够忽略不计。

页面onload事件

上面说到的两种办法还有一个缺点:会影响到页面的onload事件。这对第1方JS可能影响,由于第1方JS大都是页面重点规律,从业务规律来讲它们的加载影响到页面onload事件触发不会有问题。但第三方JS则不同样,曾经由于Google被墙GA(Google Analytics简叫作)的加载就会尤其乃至失败。引起非常多运用了GA的页面加载尤其”慢”,页面始终处在loading状态。大众经过fiddler代理来设置test.js的加载时间为10秒,而后打开之前的DEMO,查看页面的loading是不是会被延长。下面是我打开第1个DEMO的结果:

能够看到由于test.js的下载速度变慢,全部页面始终处在loading状态。页面的load事件要等到所有加载完成之后才会触发。倘若页面中的重点规律是在页面load之后再执行,那样页面很可能会在很长一段时间内不可用。极重的影响了用户的运用体验。

Friendly IFrame办法

认识决这个问题,meebo的工程师想了一个方法处理这个问题:

(function(url){

// 第1部分

vardom,doc,where,iframe = document.createElement(iframe);

iframe.src = "javascript:false";

iframe.title = "";iframe.role="presentation";

(iframe.frameElement || iframe).style.cssText = "width: 0; height: 0; border: 0";

where = document.getElementsByTagName(script);

where = where[where.length - 1];

where.parentNode.insertBefore(iframe,where);

// 第二部分

try{

doc = iframe.contentWindow.document;

}catch(e){

// IE下倘若主页面修改过document.domain,那样拜访用js创建的匿名iframe会出现跨域问题,必须经过js伪协议修改iframe内部的domain

dom = document.domain;

iframe.src="javascript:var d=document.open();d.domain="+dom+";void(0);";

doc = iframe.contentWindow.document;

}

doc.open()._l = function(){

varjs = this.createElement("script");

if(dom)this.domain = dom;

js.id = "js-iframe-async";

js.src = url;

this.body.appendChild(js);

};

doc.write(<body onload="document._l();">);

doc.close();

})(test.js);

以上代码分为两个部分:

创建了一个隐匿的iframe标签,设置其src值为JS代码,而后插进到主页面中

在iframe标签load之后加载JS脚本

这般加载Javascript,就不会阻止浏览器的onload事件,提高普通用户的体验。还有另一个好处:第三方的Javascript代码在独立的iframe中运行,不会与主页面中的JS相互干扰。已然有了有些基于这个想法的开源实现,例如:lightning.js是一个专用于快速、安全、异步地加载第三方JS代码的库。

这个办法不完美,它需要创建一个iframe标签引起了开销很强同期还需要第三方JS本身的支持。第三方JS代码运行在iframe中,引起没法获取到页面上的信息。虽然它并非跨域能够得到window.parent,然则第三方代码并不可晓得自己是不是在iframe中,需要在加载第三方JS代码的时候通告它。详细通告办法千变万化,而第三方JS的内容又不受咱们掌控

媒介宣传JS(用于展示交互宣传的JS)通常都会运行在隔离环境里面,且不需要(不准许拜访外边的window对象。倘若你需要加载的第三方JS所有宣传时,那样运用这个方法是OK的,否则并不是最为合适。幸运的是有一个叫iAB(The Interactive Advertising Bureau,简叫作iAB)的组织,创立了一套工业级标准。虽然标准已然比较旧了,然则里面说到经过设置变量inDapIF为true来通告第三方JS:你此刻正运行在iframe中。由于iAB成员较多影响力大,因此遵循这个标准是有好处的,比起自己玩一套要好的多。

总结

觉得本文对你有帮忙?请分享给更加多

关注「前端大全」,提高前端技能

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|Archiver|手机版|小黑屋|外链论坛 ( 非经营性网站 )|网站地图

GMT+8, 2024-10-3 17:26 , Processed in 0.074321 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.