模块化工具类实现方式

基于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其内部也是用类似的方法实现监控文件的加载情况的。还想写点什么,一想到马上就要回家过年了,算了结尾收工吧!

转载于:https://www.cnblogs.com/qmtx3/p/5665140.html

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

  1. 网页性能优化之异步加载js文件

    一个网页的有很多地方可以进行性能优化,比较常见的一种方式就是异步加载js脚本文件.在谈异步加载之前,先来看看浏览器加载js文件的原理. 浏览器加载 JavaScript 脚本,主要通过<scri ...

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

    方法一,jQuery.getScript HTML 代码: 代码如下 复制代码 <button id="go">Run</button> <div c ...

  3. 异步加载js文件并执行js方法:实现异步处理网页的复杂效果

    异步加载js文件并执行js方法:实现异步处理网页的复杂效果 有这么一个场景,当你的网页页面效果过多就会造成了打开页面的速度变得缓慢,长时间处于加载的状态,这样的效果通常会让用户感到不友好,通常的处理方 ...

  4. 利用jQuery的deferred异步按顺序加载JS文件

    前段时间看了阮一峰的jQuery的deferred对象详解一文,对jQuery中的deferred的用法了一些了解,今天看到园子里的一篇文章:关于重构JS前端框架的失败经验(顺便怀念那些死去的代码), ...

  5. JavaScript动态加载js文件

    /********************************************************************** JavaScript动态加载js文件* 说明:* 之前没 ...

  6. js如何动态的加载js文件

    在这个地方我说的动态的加载js文件是通过调用函数来加载js文件,我们在这个地方通过一个简单的小例子来实现 首先创建3个文件分别为:test1.html,test1.js,demo.js test1.j ...

  7. ExtJS4.x动态加载js文件

    动态加载js文件是ext4.x的一个新特性,可以有效的减少浏览器的压力,提高渲染速度.如动态加载自定义组件 1.在js/extjs/ux目录下,建立自定义组件的js文件. 2.编写MyWindow.j ...

  8. javascript:重新加载js文件

    //重新加载js文件         function loadJs(file) {             var head = $("head").remove("s ...

  9. html加载js文件失败,firefox/chrome动态设置script加载js文件失败

    firefox,chrome等w3c浏览器下面,设置script标签的src来动态加载js文件时,有2中情况 1)如果script标签已经加载过js文件,那么重新设置为其他js文件的路径时,无法加载这 ...

  10. php动态页面加载慢,通过动态加载JS文件提升网站访问速度

    相对与HTML,CSS,javascript是最影响浏览器性能的,因为浏览器在遇到<script>标签时,必须等待js代码下载和执行完毕后再执行后面的内容,因此当页面中js文件过多时,网站 ...

最新文章

  1. golang 判断文件或文件夹是否存在
  2. MapReduce自定义二次排序流程
  3. Stanford UFLDL教程 栈式自编码算法
  4. C语言 2048小游戏
  5. Citrix Netscaler版本管理和选择
  6. Linux学习笔记---移植官方uboot步骤(二)
  7. 55. 安全 HTTP(3)
  8. 阿里云-高性能计算招聘
  9. Angular和Vue.js 深度对比
  10. 集成电路模拟版图入门-版图基础学习笔记(四)
  11. ubuntu 20.04 | 美化主题、图标、光标、壁纸、登录背景
  12. 怎样正确安装Photoshop CS6破解版【图文教程】
  13. Java版实现一个简单的电话簿
  14. Native开发工具之静态库和动态库(二,小码农也有大梦想
  15. 使用QT+webassembly构建在线报表设计器
  16. 机器学习中向量函数的求导问题
  17. 音频信号输入itc服务器,ITC公共广播系统
  18. oracle对成绩开根号运算,Oracle SQL 之 数学计算-开方根(咋个办呢 zgbn)
  19. 基于C语言的网络编程的项目
  20. LLVM 目标无关代码生成器

热门文章

  1. python菜鸟入门知识
  2. CSS 常见的8种选择器 和 文本溢出问题
  3. C语言实例解析精粹学习笔记——36(模拟社会关系)
  4. 图书管理系统(一):出版社列表增加、删除和编辑
  5. 案例33-用户退出功能
  6. 关于console.log() 打印得引用类型得数据得相关问题
  7. 求数组子序列和最大值
  8. POJ-1149(网络流)
  9. [Flex]浅析Mate flex framework在实际项目中的应用(二)
  10. Python常用正则表达式语法和写法