详解JS异步加载的三种方式

一:同步加载

我们平时使用的最多的一种方式。

<script src="http://yourdomain.com/script.js"></script>
<script src="http://yourdomain.com/script.js"></script>

同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作。所以默认同步执行才是安全的。但这样如果js中有输出document内容、修改dom、重定向等行为,就会造成页面堵塞。所以一般建议把<script>标签放在<body>结尾处,这样尽可能减少页面阻塞。

二:异步加载

异步加载又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有三种方式。

方法一:也叫Script DOM Element

(function(){
  var scriptEle = document.createElement("script");
  scriptEle.type = "text/javasctipt";
  scriptEle.async = true;
  scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
  var x = document.getElementsByTagName("head")[0];
  x.insertBefore(scriptEle, x.firstChild);
 })();
<async>属性是HTML5中新增的异步支持。此方法被称为Script DOM Element 方法。Google Analytics 和 Google+ Badge 都使用了这种异步加载代码
(function(){;
  var ga = document.createElement('script');
  ga.type = 'text/javascript';
  ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0];
  s.parentNode.insertBefore(ga, s);
})();

但是这种加载方式执行完之前会阻止onload事件的触发,而现在很多页面的代码都在onload时还执行额外的渲染工作,所以还是会阻塞部分页面的初始化处理。

方法二:onload时的异步加载

(function(){
 if(window.attachEvent){
   window.attachEvent("load", asyncLoad);
  }else{
   window.addEventListener("load", asyncLoad);
  }
  var asyncLoad = function(){
   var ga = document.createElement('script');
    ga.type = 'text/javascript';
    ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(ga, s);
  }
)();

这种方法只是把插入script的方法放在一个函数里面,然后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题。

注:DOMContentLoaded与load的区别。前者是在document已经解析完成,页面中的dom元素可用,但是页面中的图片,视频,音频等资源未加载完,作用同jQuery中的ready事件;后者的区别在于页面所有资源全部加载完毕。

方法三:其他方法

由于JavaScript的动态性,还有很多异步加载方法: XHR Injection、 XHR Eval、 Script In Iframe、 Script defer属性、 document.write(script tag)。

XHR Injection(XHR 注入):通过XMLHttpRequest来获取JavaScript,然后创建一个script元素插入到DOM结构中。ajax请求成功后设置script.text为请求成功后返回的responseText。

 //获取XMLHttpRequest对象,考虑兼容性。
  var getXmlHttp = function(){
    var obj;
    if (window.XMLHttpRequest)
      obj = new XMLHttpRequest();
    else
      obj = new ActiveXObject("Microsoft.XMLHTTP");
    return obj;
  };
  //采用Http请求get方式;open()方法的第三个参数表示采用异步(true)还是同步(false)处理
  var xmlHttp = getXmlHttp();
  xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
  xmlHttp.send();
  xmlHttp.onreadystatechange = function(){
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
      var script = document.createElement("script");
      script.text = xmlHttp.responseText;
      document.getElementsByTagName("head")[0].appendChild(script);
    }
  }
XHR Eval:与XHR Injection对responseText的执行方式不同,直接把responseText放在eval()函数里面执行。
  //获取XMLHttpRequest对象,考虑兼容性。
  var getXmlHttp = function(){
    var obj;
    if (window.XMLHttpRequest)
      obj = new XMLHttpRequest();
    else
      obj = new ActiveXObject("Microsoft.XMLHTTP");
    return obj;
  };
  //采用Http请求get方式;open()方法的第三个参数表示采用异步(true)还是同步(false)处理
  var xmlHttp = getXmlHttp();
  xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
  xmlHttp.send();
  xmlHttp.onreadystatechange = function(){
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
      eval(xmlHttp.responseText);
      //alert($);//可以弹出$,表明JS已经加载进来。click事件放在其它出会出问题,应该是还没加载进来
      $("#btn1").click(function(){
        alert($(this).text());
      });
    }
  } 

Script In Irame:在父窗口插入一个iframe元素,然后再iframe中执行加载JS的操作。

 var insertJS = function(){alert(2)};
  var iframe = document.createElement("iframe");
  document.body.appendChild(iframe);
  var doc = iframe.contentWindow.document;//获取iframe中的window要用contentWindow属性。
  doc.open();
  doc.write("<script>var insertJS = function(){};<\/script><body onload='insertJS()'></body>");
  doc.close();

GMail Mobile:业内JS内容被注释,所以不会执行,在需要的时候,获取script中的text内容去掉注释,调用eval()执行。

  <script type="text/javascript">
  /*
  var ...
  */
  </script>

    HTML5新属性:async和defer属性

defer属性:IE4.0就出现。defer属声明脚本中将不会有document.write和dom修改。浏览器会并行下载其他有defer属性的script。而不会阻塞页面后续处理。注:所有的defer脚本必须保证按顺序执行的。

  <script type="text/javascript" defer></script>

async属性:Html5新属性。脚本将在下载后尽快执行,作用同defer,但是不能保证脚本按顺序执行。他们将在onload事件之前完成。

  <script type="text/javascript" defer></script>

Firefox 3.6、Opera 10.5、IE 9和最新的Chrome和Safari都支持async属性。可以同时使用async和defer,这样IE 4之后的所有IE都支持异步加载。

没有async属性,script将立即获取(下载)并执行,期间阻塞了浏览器的后续处理。如果有async属性,那么script将被异步下载并执行,同时浏览器继续后续的处理。

总结: 对于支持HTML5的浏览器,实现JS的异步加载只需要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持Html5的浏览器(IE可以用defer实现),可以采用以上几种方法实现。原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,你可以把它放在匿名函数中执行,也可以在onload中执行,也可以通过XHR注入实现,也可以创建一个iframe元素,然后在iframe中执行插入JS代码。

三:延迟加载

有些JS代码在某些情况在需要使用,并不是页面初始化的时候就要用到。延迟加载就是为了解决这个问题。将JS切分成许多模块,页面初始化时只加载需要立即执行的JS,然后其它JS的加载延迟到第一次需要用到的时候再加载。类似图片的延迟加载。
JS的加载分为两个部分:下载和执行。异步加载只是解决了下载的问题,但是代码在下载完成后就会立即执行,在执行过程中浏览器处于阻塞状态,响应不了任何需求。

解决思路:为了解决JS延迟加载的问题,可以利用异步加载缓存起来,但不立即执行,需要的时候在执行。如何进行缓存呢?将JS内容作为Image或者Object对象加载缓存起来,所以不会立即执行,然后在第一次需要的时候在执行。

1:模拟较长的下载时间:

利用thread让其sleep一段时间在执行下载操作。

2:模拟较长的JS代码执行时间

  var start = Number(new Date());
  while(start + 5000 > Number(new Date())){//执行JS}

这段代码将使JS执行5秒才完成!

JS延迟加载机制(LazyLoad):简单来说,就是在浏览器滚动到某个位置在触发相关的函数,实现页面元素的加载或者某些动作的执行。如何实现浏览器滚动位置的检测呢?可以通过一个定时器来实现,通过比较某一时刻页面目标节点位置和浏览器滚动条高度来判断是否需要执行函数。

以上所述是小编给大家介绍的详解JS异步加载的三种方式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • javascript写的异步加载js文件函数(支持数组传参)

    自己用的加载js文件,支持多文件,不兼容ie 复制代码 代码如下: /** * 加载js文件 * @param  {string || array}   url   js路径 * @param  {Function} fn      加载完成后回调 * @return {object}           game对象 * @example * getScript("url.js",fn) * getScript(["url-1.js","url-2.js

  • asp.net 使用js分页实现异步加载数据

    1.准备工作 引入"jquery-1.8.3.min.js",AjaxPro.2.dll":用于前台js调用后台方法. 2.Web.config的配置 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <clear/> <!-- 数据库链接 --> <

  • 如何调试异步加载页面里包含的js文件

    最近在一个新的web项目中开发功能.这个项目的管理界面有一个特点,框架是固定的,不会刷新,每次点新的页面仅仅刷新一个div.div里面不是套的iframe,于是导致了一个问题,用浏览器无法调试异步加载页面里包含的js文件.简单的说就是在调试工具里面看不到异步加载页面里包含的js文件. 网上找到了一个解决办法,就是在需要调试的js文件顶部加一行代码: //@ sourceURL=msgprompt.js 注意,@符号和sourceURL间必须有空格.Chrome下效果: FireFox下也是OK的

  • struts2+jquery+json实现异步加载数据(自写)

    复制代码 代码如下: //初始加载页面时 $(document).ready(function(){ //为获取单个值的按钮注册鼠标单击事件 $("#getMessage").click(function(){ $.getJSON("ceshi",function(data){ //通过.操作符可以从data.message中获得Action中message的值 $("#message1").html("<font color='

  • 使用jQuery异步加载 JavaScript脚本解决方案

    JavaScript 加载器在 Web 开发中是非常强大和有用的工具.目前流行的几个加载器,像 curljs.LABjs 和 RequireJS 使用都很广泛.他们功能强大的,但有些情况下可以有更简单的方案. 如果你正在使用 jQuery,有一个内置的方法可以用来加载脚本.如果你想延迟加载插件或任何其他类型的脚本,都可以使用这种方法.下面给大家介绍如何使用它. 实现方法 jQuery 内置有 getScript 方法来加载一个脚本,处理返回的结果可以有几种方法来实现.最基本的用法 jQuery.

  • javascript loadScript异步加载脚本示例讲解

    一.语法:loadScript(url[,callback])或者loadScript(settings)二.settings支持的参数:url:脚本路径async:是否异步,默认false(HTML5)charset:文件编码cache:是否缓存,默认为truesuccess:加载成功后执行的函数,优先执行callback.三.调用举例: 复制代码 代码如下: //loadScript(url[,callback])loadScript("http://code.jquery.com/jque

  • 点评js异步加载的4种方式

    js异步加载的4种方式,点评开始. 方案1:$(document).ready <!DOCTYPE html> <html> <head> <script src="http://common.cnblogs.com/script/jquery.js" type="text/javascript"></script> <script type="text/javascript"&g

  • AngularJS 实现按需异步加载实例代码

    AngularJS 通过路由支持多视图应用, 可以根据路由动态加载所需的视图, 在 AngularJS 的文档中有详细的介绍, 网上也有不少教程, 就不用介绍了! 随着视图的不断增加,js文件会越来越多,而 AngularJS 默认需要把全部的js都一次性加载,使用起来非常不便, 因此按需加载模块的需求会越来越强,不过,AngularJS 并没有实现按需加载. 习惯了 seajs 的异步加载方式,也想着 angular 也能同样使用异步加载,但是事实不随人愿. angularjs 和 requi

  • JavaScript异步加载浅析

    前言 关于JavaScript脚本加载的问题,相信大家碰到很多.主要在几个点-- 1> 同步脚本和异步脚本带来的文件加载.文件依赖及执行顺序问题 2> 同步脚本和异步脚本带来的性能优化问题 深入理解脚本加载相关的方方面面问题,不仅利于解决实际问题,更加利于对性能优化的把握并执行.   先看随便一个script标签代码-- 复制代码 代码如下: <script src="js/myApp.js"></script> 如果放在<head>上面

  • 详解JS异步加载的三种方式

    一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输

  • JS异步加载的三种实现方式

    js加载的缺点:加载工具方法没必要阻塞文档,过多js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作. 有些工具方法需要按需加载,用到再加载,不用不加载,. 默认正常模式下下,JS是同步加载的,即优先加载JS,只有当JS文件下载完,dom和css才开始加载,当某些时候我们需要JS异步加载,我们可以通过以下方式来设置异步加载,不同情况下选取不同方式即可 1.defer:defer JS异步下载,dom结构解析完(标签 + 样式(内容不一定下载完))才异步执行 仅I

  • 详解react-router4 异步加载路由两种方法

    方法一:我们要借助bundle-loader来实现按需加载. 首先,新建一个bundle.js文件: import React, { Component } from 'react' export default class Bundle extends React.Component { state = { // short for "module" but that's a keyword in js, so "mod" mod: null } componen

  • js异步加载的三种解决方案

    默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的. (1) defer,只支持IE defer属性的定义和用法(我摘自w3school网站) defer 属性规定是否对脚本执行进行延迟,直到页面加载为止. 有的 javascript 脚本 document.write 方法来创建当前的文档内容,其他脚本

  • 详解Nginx 虚拟主机配置的三种方式(基于IP)

    Nginx配置虚拟主机支持3种方式:基于IP的虚拟主机配置,基于端口的虚拟主机配置,基于域名的虚拟主机配置. 详解Nginx 虚拟主机配置的三种方式(基于端口) https://www.jb51.net/article/14977.htm 详解Nginx 虚拟主机配置的三种方式(基于域名) https://www.jb51.net/article/14978.htm 1.基于IP的虚拟主机配置 如果同一台服务器有多个IP,可以使用基于IP的虚机主机配置,将不同的服务绑定在不同的IP上. 1.1

  • 详解Redis集群搭建的三种方式

    一.单节点实例 单节点实例还是比较简单的,平时做个测试,写个小程序如果需要用到缓存的话,启动一个 Redis 还是很轻松的,做为一个 key/value 数据库也是可以胜任的 二.主从模式(master/slaver) redis 主从模式配置 主从模式: redis 的主从模式,使用异步复制,slave 节点异步从 master 节点复制数据,master 节点提供读写服务,slave 节点只提供读服务(这个是默认配置,可以通过修改配置文件 slave-read-only 控制).master

  • 详解Spring Boot 访问Redis的三种方式

    目录 前言 开始准备 RedisTemplate JPA Repository Cache 总结 前言 最近在极客时间上面学习丁雪丰老师的<玩转 Spring 全家桶>,其中讲到访问Redis的方式,我专门把他们抽出来,在一起对比下,体验一下三种方式开发上面的不同, 分别是这三种方式 RedisTemplate JPA Repository Cache 开始准备 开始之前我们需要有Redis安装,我们采用本机Docker运行Redis, 主要命令如下 docker pull redis doc

  • 详解Nginx 虚拟主机配置的三种方式(基于端口)

    Nginx配置虚拟主机支持3种方式:基于IP的虚拟主机配置,基于端口的虚拟主机配置,基于域名的虚拟主机配置. 详解Nginx 虚拟主机配置的三种方式(基于IP) https://www.jb51.net/article/14974.htm 详解Nginx 虚拟主机配置的三种方式(基于域名) https://www.jb51.net/article/14978.htm 2.Nginx基于端口的虚拟主机配置 如一台服务器只有一个IP或需要通过不同的端口访问不同的虚拟主机,可以使用基于端口的虚拟主机配

  • 详解Golang开启http服务的三种方式

    前言 都说go标准库实用,Api设计简洁.这次就用go 标准库中的net/http包实现一个简洁的http web服务器,包括三种版本. v1最简单版 直接使用http.HandleFunc(partern,function(http.ResponseWriter,*http.Request){}) HandleFunc接受两个参数,第一个为路由地址,第二个为处理方法. //v1 func main() { http.HandleFunc("/", func(w http.Respon

随机推荐