虽然没有真正的理解其实现细节,但是对其源码组织以及基本的逻辑执行有了整体的了解。
本文是参考网上的源码,分析其思想实现的简易的模块加载器,旨在加深对于require.js的认知与理解。
实现思路首先明确的几点如下:
每调用一次require函数,就会创建一个context对象
module对象表示模块对象,基本的操作都是该对象的原型方法
上面两个对象是实现该简易模块加载器核心,加载过程中的处理流程如下图所示:
module对象的属性有:
mid:表示模块id
src:模块路径
name: 模块名称
deps:模块的依赖列表
callback:回调函数
errback:错误处理函数
status:模块状态
exports:与回调函数参数序列对应的模块输出
module的原型对象有如下几个方法:
init:module对象的初始化处理
fetch:创建script节点并追加到元素节点上
checkcycle:处理循环依赖,返回当前的循环依赖列表
handledeps:处理依赖列表
changestatus:改变模块的状态,主要处理模块是否加载成功
execute:依赖的模块都加载成功后,参数列表的获取
如何处理依赖列表的事实上,处理依赖列表是define以及require中处理,define函数以及require函数的处理代码如下:
首先看require函数,实例:
require(['a', 'b'], function(a, b) { console.log(a, b); });
从上面可以看出,调用require函数,依赖列表是[‘a’, ‘b’],回调函数是function(a, b) {console.log(a, b);}
从require代码可以就看出,调用context构造函数创建一个context对象,现在看下context构造函数的处理:let context = function(deps, callback, errback) {
this.cid = ++contextid; this.init(deps, callback, errback); } ; context.prototype.init = function(deps, callback, errback) { this.deps = deps; this.callback = callback; this.errback = errback; contexts[this.cid] = this; } ;
上面中重要的是contexts[this.cid] = this;将当前的context对象注册到全局contexts对象集合中。
然后调用handledeps函数,该函数处理依赖列表,具体的代码如下:handledeps: function() {
let depcount = this.deps ? this.deps.length : 0; // require.js中处理循环依赖的处理 let requireindep = (this.deps || []).indexof('require'); if (requireindep !== -1) { depcount--; this.requireindep = requireindep; this.deps.splice(requireindep, 1); } // 处理循环依赖情况 let cyclearray = this.checkcycle(); if (cyclearray) { depcount = depcount - cyclearray.length; } // depcount表示当前模块的依赖模块数,depcount为0表示模块中某一依赖加载完成 this.depcount = depcount; if (depcount === 0) { this.execute(); return; } // 遍历依赖列表,创建module对象,并且将当前模块与其依赖的关系构建出来maps this.deps.foreach((depmodulename) => { if (!modules[depmodulename]) { let module = new module(depmodulename); modules[module.name] = module; } if (!maps[depmodulename]) { maps[depmodulename] = []; } maps[depmodulename].push(this); } );
}循环依赖的处理
本次实现代码中的循环依赖的处理,是require.js中官方的方法,就是传递require在回调函数中再次require,为什么这样就可以解决循环依赖?
就本次实现而言,因为require一次就会创建一个context对象。主要代码如下:// require.js中处理循环依赖的处理let requireindep = (this.deps || []).indexof('require');
if (requireindep !== -1) { depcount--; this.requireindep = requireindep; this.deps.splice(requireindep, 1); } // 获取循环依赖 let cyclearray = this.checkcycle(); if (cyclearray) { depcount = depcount - cyclearray.length; } // execute函数中代码// 插入require到回调函数的参数列表中if (this.requireindep !== -1 && this.requireindep !== undefined) { arg.splice(this.requireindep, 0, require); }
结束语
纸上得来终觉浅,觉知此事要躬行,通过实现简易的模块加载器,对于require.js模块加载的思想以及逻辑处理更加地清晰。
虽然与require.js对于js文件的异步加载的处理方式不同,但是本质是一样的,require.js是添加script节点到head标签中,并且script添加async属性来实现异步加载的。
以上就是用js实现简易模块加载器的方法的详细内容。
