第四课:seajs的模块编译_compile过程

seajs的模块编译_compile过程。

a.js的编译

Module.prototype._compile = function() {
var module = this
// 如果该模块已经编译过,则直接返回module.exports
if (module.status === STATUS.COMPILED) {
return module.exports
}
// 1. the module file is 404.
// 2. the module file is not written with valid module format.
// 3. other error cases.
// 这里是处理一些异常情况,此时直接返回null
if (module.status < STATUS.SAVED && !hasModifiers(module)) {
return null
}
// 更改模块状态为COMPILING,表示模块正在编译
module.status = STATUS.COMPILING

 // 模块内部使用,是一个方法,用来获取其他模块提供(称之为子模块)的接口,同步操作
 function require(id) {
    // 根据id解析模块的路径
     var uri = resolve(id, module.uri)
     // 从模块缓存中获取模块(注意,其实这里子模块作为主模块的依赖项是已经被下载下来的)
     var child = cachedModules[uri]

    // Just return null when uri is invalid.
     // 如果child为空,只能表示参数填写出错导致uri不正确,那么直接返回null
     if (!child) {
       return null
     }

     // Avoids circular calls.
     // 如果子模块的状态为STATUS.COMPILING,直接返回child.exports,避免因为循环依赖反复编译模块
     if (child.status === STATUS.COMPILING) {
       return child.exports
     }
     // 指向初始化时调用当前模块的模块。根据该属性,可以得到模块初始化时的Call Stack.
     child.parent = module
     // 返回编译过的child的module.exports
     return child._compile()
}
 // 模块内部使用,用来异步加载模块,并在加载完成后执行指定回调。
 require.async = function(ids, callback) {
   module._use(ids, callback)
 }
 // 使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。
 require.resolve = function(id) {
   return resolve(id, module.uri)
 }
 // 通过该属性,可以查看到模块系统加载过的所有模块。
 // 在某些情况下,如果需要重新加载某个模块,可以得到该模块的 uri, 然后通过 delete require.cache[uri] 来将其信息删除掉。这样下次          使用时,就会重新获取。
 require.cache = cachedModules

 // require是一个方法,用来获取其他模块提供的接口。
 module.require = require
 // exports是一个对象,用来向外提供模块接口。
 module.exports = {}
 var factory = module.factory

 // factory 为函数时,表示模块的构造方法。执行该方法,可以得到模块向外提供的接口。
 if (util.isFunction(factory)) {
   compileStack.push(module)
   runInModuleContext(factory, module)
   compileStack.pop()
 }
 // factory 为对象、字符串等非函数类型时,表示模块的接口就是该对象、字符串等值。
 // 如:define({ "foo": "bar" });
 // 如:define('I am a template. My name is {{name}}.');
 else if (factory !== undefined) {
   module.exports = factory
 }

 // 更改模块状态为COMPILED,表示模块已编译
 module.status = STATUS.COMPILED
 // 执行模块接口修改,通过seajs.modify()
 execModifiers(module)
 return module.exports

}
if (util.isFunction(factory)) {
compileStack.push(module)
runInModuleContext(factory, module)
compileStack.pop()
}
这里就是把module.export进行初始化。runInModuleContext方法:
// 根据模块上下文执行模块代码
function runInModuleContext(fn, module) {
// 传入与模块相关的两个参数以及模块自身
// exports用来暴露接口
// require用来获取依赖模块(同步)(编译)
var ret = fn(module.require, module.exports, module)
// 支持返回值暴露接口形式,如:
// return {
// fn1 : xx
// ,fn2 : xx
// …
// }
if (ret !== undefined) {
module.exports = ret
}
}
执行a.js中的function方法,这时会调用var b = require(“b.js”),
require方法会返回b的compile方法的返回值,b模块中又有var c = require(‘c.js’)。
这时会调用c的compile方法,然后调用c的function,c中,如果要暴露对象,或者是return 对象c,则模块c的exports = c。或者直接是module.export = c;总之最后会返回module c.export = c;所以var c = module c.export = c,模块b中,就可以使用变量c调用模块c中的c对象的方法和属性。
以此类推,最终a模块也能调用b模块中b对象的属性和方法。
不管什么模块,只要使用了module.export = xx模块,其他模块就可以使用require(“xx模块”),调用xx模块中的各种方法了。
最终模块的状态会变成module.status = STATUS.COMPILED。
Module.prototype._use = function(ids, callback) {  
  var uris = resolve(ids, this.uri); //解析[‘./a’,’jquery’]
this._load(uris, function() { //把解析出来的a,jquery模块的地址[url1,url2],调用_load方法。
          //util.map : 让数据成员全部执行一次一个指定的函数,并返回一个新的数组,该数组为原数组成员执行回调后的结果
var args = util.map(uris, function(uri) {

     return uri ? cachedModules[uri]._compile() : null;//如果存在url,就调用_compile方法。

   })
   if (callback) { callback.apply(null, args) } 
  })
}

这时args = [module a.export, module jquery.export];
seajs.use([‘./a’,’jquery’],function(a,){  
    var num = a.a;  
    $(‘#J_A’).text(num);  
})  
这时function中的a和
){      var num = a.a;      $(‘#J_A’).text(num);  })  这时function中的a和
就是module a.export和module jquery.export。

因为本人现在在研究jquery源码和jquery框架设计,因此共享一些经验:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值