实现异步加载js文件及加载完成后回调

模块化工具类实现方式

基于AMD、CMD模式的JS模块化管理工具越来越流行,这些工具通常只需在页面中加载对应的工具JS,其他JS文件都是异步加载的,比如RequireJS就可以象下面这样做。

首先在页面加载

<script data-main="scripts/main.js" src="scripts/require.js"></script>

然后工具会自动识别data-main属性值,并加载对应的JS文件,在main.js可以加载更多模块来实现复杂的业务。这里仅加载一个jQuery模块


1

2

3

4

5

6

7

8

require.config({

    paths: {

        ‘jquery‘: ‘lib/jquery-1.11.1.min‘

    }

});

require([‘jquery‘], function($) {

    console.log(‘jQuery模块文件已经加载完成‘);

});

异步加载“业务类”JS文件

前人种树,后人乘凉,牛人们开发了各种好用的工具,我们可以直接拿来用,但是我们至少应该理解其最基本原理,下面让我们一步步实现自己的JS文件异步加载工具。之所以叫“业务类”,就是那些JS不提供依赖或者说是不提供任何功能,只负责实现自己的业务。比如一个1.js文件内容如下:

alert(‘1.js文件加载了‘);

下面需要在页面中加载这个JS,通常使用如下方法:


1

2

3

4

var script = document.createElement(‘script‘);

var head = document.getElementsByTagName(‘head‘)[0];

script.src = ‘1.js‘;

head.appendChild(script);

点击查看demo,可以看出异步加载这类JS比较简单,因为只管添加到head中让浏览器加载,至于什么时候加载完,我们不用管,因为其他JS不会依赖该文件,所以不需要关注他是否加载完成。

异步加载JS文件并执行回调函数

先把上面的代码简单封装成一个函数,因为需要执行回调所以多加一个参数


1

2

3

4

5

6

7

8

9

10

function loadJS(src, callback){

    var script = document.createElement(‘script‘);

    var head = document.getElementsByTagName(‘head‘)[0];

    script.src = src;

    head.appendChild(script);

    callback();

}

loadJS(‘1.js‘, function(){

    alert(‘执行回调‘);

});

按上面代码的流程head.appendChild(script)加载脚本之后执行回调callback(),可以点击查看demo,打开页面后依次弹出‘执行回调‘、“‘1.js文件加载了‘”,并不是我想所预想的先加载1.js然后执行里面的代码最后再执行回调函数,这个问题主要因为异步加载引起的,在head.appendChild(script)之后,引擎并不会等待文件的加载和执行完成,就继续往下执行了。对于上面的代码这个问题并不会致命,但对于下面所说在这种情况就会引起报错。

异步加载“依赖类”JS文件

所谓依赖类”,就是异步加载的JS文件会提供依赖或者说是提供功能,比如一个2.js文件内容如下:


1

2

3

function sayHello(){

    alert(‘hello‘);

}

这个JS文件中包含了一个函数,而我在页面中想通过loadJS加载2.js文件,这样就可以使用这个函数了。


1

2

3

loadJS(‘2.js‘, function(){

    sayHello();

});

但由于是异步加载的,执行回调里的sayHello()时,2.js文件可能还没加载完成或者未执行,所以会报错“sayHello is not defined”,点击这里查看demo,看来还要给loadJS函数增加一个监听文件加载状态的功能,如果加载完成就执行回调。增加之前先看了下jQuery的处理方法,以下为jQuery源码中jQuery.ajaxTransport( "script"的回调代码片断:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

script = document.createElement("script");

script.async = true;

if ( s.scriptCharset ) {

    script.charset = s.scriptCharset;

}

script.src = s.url;

// Attach handlers for all browsers

script.onload = script.onreadystatechange = function( _, isAbort ) {

    if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {

        // Handle memory leak in IE

        script.onload = script.onreadystatechange = null;

        // Remove the script

        if ( script.parentNode ) {

            script.parentNode.removeChild( script );

        }

        // Dereference the script

        script = null;

        // Callback if not abort

        if ( !isAbort ) {

            callback( 200, "success" );

        }

    }

};

// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending

// Use native DOM manipulation to avoid our domManip AJAX trickery

head.insertBefore( script, head.firstChild );

发现jQuery是通过script元素的onload或者onreadystatechange事件来实现回调的。readyState和onreadystatechange属性都是IE特有的,火狐、Chrome等一些现代浏览器都没有取而代之的是onload事件,反之IE也没有onload事件,所以这里onload和onreadystatechange绑定了同一个处理函数。下面开始画瓢:


1

2

3

4

5

6

7

8

9

10

11

12

13

function loadJS(src, callback){

    var script = document.createElement(‘script‘);

    var head = document.getElementsByTagName(‘head‘)[0];

    script.src = src;

    head.appendChild(script);

    if(typeof callback === ‘function‘){

        script.onload = script.onreadystatechange = function(){

            if(!script.readyState || /loaded|complete/.test(script.readyState)){

                callback();

            }

        }

    }

}

查看demo,上面的代码不会再报错了,但测试发现IE9及以上会弹出两次hello,查了一下得知从IE9开始支持onload事件了但他同时还支持onreadystatechange,因为这里绑定了两个所以就执行了两次,解决的方法可以分开绑定或者加一个标识判断是否已经执行过回调了,个人建议使用分开绑定的方式解决,RequireJS 2.1.11也是采用的这种方式。

下面是用标识处理执行两次问题的demo,加载JS的代码如下


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

function loadJS(src, callback){

    var script = document.createElement(‘script‘);

    var head = document.getElementsByTagName(‘head‘)[0];

    var loaded;

    script.src = src;

    if(typeof callback === ‘function‘){

        script.onload = script.onreadystatechange = function(){

            if(!loaded && (!script.readyState || /loaded|complete/.test(script.readyState))){

                script.onload = script.onreadystatechange = null;

                loaded = true;

                callback();

            }

        }

    }

    head.appendChild(script);

}

看起来文章挺长内容挺多,其实就是一个异步加载的函数,也没有啥技术含量,文章开头所写的插件RequireJS其内部也是用类似的方法实现监控文件的加载情况的。还想写点什么,一想到马上就要回家过年了,算了结尾收工吧!

时间: 2024-08-02 02:51:18

实现异步加载js文件及加载完成后回调的相关文章

js判断异步引入的js文件是否加载完毕

在正常的加载过程中,js的加载都是同步的,也就是在加载过程中,浏览器会阻塞接下来的内容的加载.这时候我们就要用到动态加载,动态加载是异步的,如果我们在后边要用到这个动态加载的js文件里的东西,就要保证这个文件加载完成后,再执行下面的内容. 如何判断js是否加载完成?(实现loadScript(url,callback)异步加载脚本,完成后执行回调函数,要求支持IE) <!DOCTYPE html> <html> <head> <meta charset="

js文件最后加载(在window.load事件发生后再加载js文件),用于解决因jQuery等js库导致网页加载慢的问题

需引入文件:lazyload-min.js <script src="JS/lazyload-min.js" type="text/javascript"></script> 插入代码: function loadscript() { LazyLoad.loadOnce([ 'JS/touch.js', 'http://libs.baidu.com/jquery/1.2.3/jquery.min.js' ], loadComplete); }

异步加载js文件的方法总结

方法一,jQuery.getScript HTML 代码: 代码如下 复制代码 <button id="go">Run</button><div class="block"></div> jQuery 代码: jQuery.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color.js", function(){ $

为了提高性能,如何动态加载JS文件

超级表格是一款多人协作的在线表格,程序相当复杂,用到十几个JS文件.但是有些文件是在打开某些类型的表格时才需要加载. 例如,只有当打开甘特图表格时,才需要加载gantetu.js文件. 那么问题来了,为了减少js文件数量和请求,如何在需要时自动加载js文件呢? 办法如下. //动态加载js文件function mcss_importJS(js){ var oHead = document.getElementsByTagName('HEAD').item(0); var oScript= doc

用AngularJS构建单页应用,根据需求加载JS文件的方法

我们在AngularJS中可以根据网址不同直接切换view,动态加载网页模板,但是控制模板的控制器需要先定义好才可以,不能和网页模板同时加载,这样就造成整个网站的JS都要先加载完成. requireJS是大家第一个想到的东西,但是体积有点大,github上就有个人用它写了Angular的动态加载controller模块,是通过js promise异步编程技术实现的,最后的解决很简单~~ 首先在$routeProvider里面加resolve属性, $routeProvider. when('/p

jquery如何动态加载js文件

jquery如何动态加载js文件:本章节和大家分享一下几种动态加载js文件的方法,希望能够给需要的朋友带来帮助.方法一: $.getscript("test.js"); 方法二: function loadjs(file) { var head = $('head').remove('#loadscript'); $("<scri"+"pt>"+"</scr"+"ipt>").att

js如何判断引入的js文件是否加载完毕

js如何判断引入的js文件是否加载完毕:如果javascript代码较少的话完全可以将js代码通过<script></script>标签写在当前页面,但是如果js代码非常庞大的话,那么页面将会变得非常的臃肿,并且由于js代码是同步加载,所以当js代码加载的时候,会阻塞下面内容的解析,所以最好能够动态加载js功能,尤其是能够实现根据需要动态引入外部js文件.由于动态加载js文件是异步的,所以有时候需要判断js文件是否加载完毕,下面就通过代码介绍一下如何实现判断功能.代码如下: fun

Extjs学习----------动态加载js文件(减轻浏览器的压力)

动态加载js文件可以减轻浏览器的压力,本例使用了Ext.window.Window组件,该组件的学习地址:http://blog.csdn.net/z1137730824/article/details/38538277 具体实现步骤: (1)建立dynamic.jsp文件 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String

jQuery之JSP加载JS文件不起作用的有效解决方法

JSP加载JS文件不起作用的有效解决方法 作者: 字体:[增加 减小] 类型:转载 时间:2014-04-08 jsp导入jquery文件,老是不起作用,原因在于其不能访问/WEB-INF/目录下的文件,下面有个不错的解决方法,大家可以参考下 目录结构:jsp导入jquery文件,老是不起作用,原因在于: 复制代码 代码如下: <script type="text/javascript" src="js/jquery-1.4.2.min.js"><