有了seajs、requireJ这样的模块加载框架,一直想研究,今天先尝试自己来写一下简单的加载功能,等了解后再去看大牛们的源码,仅当作为自己学习练习,有很多考虑不周的地方请指出,主要就两个方法:
VM.define(‘模块名称’,{url:‘模块路径’,requires:‘模块依赖项’(可以是模块名的字符串,或者数组)});
VM.use(‘模块名称’,‘回调函数callback’);
一个是定义模块,一个是使用模块;使用的模块都必须先定义,
定义的时候不会加载模块,只有在使用的时候才加载模块;
1、不会出现重复加载的模块,调用过的模块不会再append第二次,不能定义相同名字的模块;
2、依赖项可以是多个,从左到右加载,多个的时候用数组传参,单个时可以用字符串传参;
3、支持链式调用,要避免循环依赖的情况;
修改:上次的代码存在考虑不周的情况,多谢园友 程序猿小卡 留言的指出,现再的也可能有问题,但是如果正常使用依赖,相信还是能够满足的,仅供研究:)
代码如下:使用方法:只需引入myModule.js文件, <script type="text/javascript" src="myModule.js"></script>
myModule.js代码如下:
1 /** 2 3 * Author : vvg 4 5 * version : 0.1.1 6 7 * name : VModule.js 8 9 **/ 10 11 (function () { 12 // 调试提示 13 var log = function (content) { 14 if (typeof console.log === 'function') { 15 console.log(content); 16 } else { 17 alert(content); 18 } 19 } 20 21 var createScript = function (url) { 22 var script = document.createElement('script'); 23 script.type = 'text/javascript'; 24 script.src = url; 25 return script; 26 }; 27 28 var head = document.getElementsByTagName('head')[0]; 29 var toString = Object.prototype.toString; 30 31 var VModule = {}; 32 /** 33 * 定义模块 34 * @param name {string} 35 * @param options {object} url/requires 36 */ 37 VModule.define = function (name, options) { 38 //定义模块名称、地址和依赖 39 if (!this.modules) this.modules = {}; 40 if (this.modules[name]) { 41 log(name + '已经存在,请更换名称.'); 42 return; 43 } 44 this.modules[name] = options; 45 // 是否加载 46 this.modules[name].isLoad = false; 47 // 是否使用 48 this.modules[name].isUse = false; 49 // 回调队列 50 this.modules[name].callBackQueue = []; 51 return this; 52 } 53 54 VModule.use = function (name, func) { 55 var len, self = this; 56 if (!this.modules[name]) { 57 log(name + '不存在.'); 58 return this; 59 } 60 // 回调队列,用于多次use同一个模块时的多个回调 61 var callBackQueue = this.modules[name].callBackQueue; 62 if (!this.modules[name].isUse) { 63 // 标记模块已经使用过 64 this.modules[name].isUse = true; 65 // 推入队列 66 callBackQueue.push(func); 67 var url = this.modules[name].url; 68 var requires = this.modules[name].requires; 69 70 // 串行依赖情况 71 if (toString.call(requires) == '[object String]') { 72 this.use(requires, function () { 73 self.load(name, callBackQueue); 74 }); 75 return this; 76 } 77 78 // 并行依赖处理 79 if (toString.call(requires) == '[object Array]') { 80 //循环查找 81 len = requires.length; 82 this.modules[name].count = len; 83 for (var i = 0; i < len; i++) { 84 var self = this; 85 this.use(requires[i], function () { 86 VModule.modules[name].count--; 87 // 串行依赖即等待所有的文件加载完毕后才执行回调 88 if (VModule.modules[name].count == 0) { 89 self.load(name,callBackQueue); 90 } 91 }) 92 } 93 return this; 94 } 95 this.load(name, callBackQueue); 96 } else { 97 // 如果模块已经标记使用,但是模块还未下载完毕时,加入队列, 如果下载完毕则直接执行回调函数 98 if(!this.modules[name].isLoad){ 99 func && callBackQueue.push(func); 100 }else{ 101 func && func(); 102 } 103 return this; 104 } 105 } 106 VModule.load = function (name, callBackQueue) { 107 if (!this.modules[name].isLoad) { 108 var self = this; 109 var script = createScript(self.modules[name].url); 110 script.onload = script.onreadystatechange = function () { 111 if ((!this.readyState) || this.readyState === "loaded" || this.readyState === "complete") { 112 self.modules[name].isLoad = true; 113 // 循环调用回调队列 114 for(var i = 0, n = callBackQueue.length;i<n;i++){ 115 callBackQueue[i](); 116 } 117 } 118 } 119 head.appendChild(script); 120 } 121 } 122 123 window.VM = VModule; 124 125 })();
我的测试DEMO:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>js模块化</title> <script type="text/javascript" src="Module.js"></script> </head> <body> <p id="jq">测试</p> <p id="jq2">测试</p> <script type="text/javascript"> VM.define('a', {url:'moduleA.js', requires:['b', 'd', 'c']}) .define('b', {url:'moduleB.js'}) .define('c', {url:'http://common.cnblogs.com/script/jquery.js?178979879891'}) .define('d', {url:'moduleD.js', requires:['e', 'g', 'f']}) .define('e', {url:'moduleE.js', requires:'f'}) .define('f', {url:'moduleF.js'}).define('g', {url:'moduleG.js'}); VM.use('a', function () { $('#jq').html('JQ下载成功!!').css('color', 'red'); }).use('a', function () { $('#jq2').html('JQ下载成功!!').css('color', 'red'); }).use('a',function(){ $('#jq2').append('<em>第三次加载</em>').find('em').css('color','blue'); }).use('f',function(){ console.log('F加载成功!'); }) </script> </body> </html>
DEMO下载地址:点我点我点我