代码概览
src目录文件列表以下:
代码以模块化的方式来组织,构建的时候会合并为一个js文件(sea.js 或 sea-debug.js),其中,intro.js和 outro.js 分别是这个js文件的头部和尾部。
若是习惯看一个文件的代码,能够直接阅读dist目录下的 sea-debug.js , 这个是 全部模块合并后的代码 。
sea.js 记录了当前的版本,“@VERSION”在构建的时候应该会被替换为具体的版本号。
util 开头的文件是一些工具方法,好比 路径的转换、语法的加强、事件等;
util-lang.js 实现了 isObject 、isString、isArray、isFunction isUndefined 等用于判断对象类型的方法。
util-events.js 实现了事件机制,为 seajs 对象添加了三个方法:on、off、emit;
on 函数用于添加一个事件监听函数,off 用于移除监听函数,emit用于触发一个事件。
全部添加的事件监听函数,都被存储在一个 events 二维数组中,当一个事件被emit后,会在events找到为该事件添加的全部监听函数,而后循环执行。
util-path.js 有一些用于处理文件路径相关的方法,好比 id2Url 能够把一个模块的id转换为一个完整的url;seajs.resolve(Module.resolve) 方法 其实就指向这里的 id2Url。
util-request.js 实现了 seajs.request 方法,用于根据url 加载js脚本;此方法会判断当前浏览器是否支持 webWorker ,若是支持,则使用importScripts的方式加载脚本,若是不支持web worker,则采用建立 script 节点的方式实现js脚本的加载,为了防止在IE中的内存泄露,在脚本加载完onload后,会及时移除该节点。
util-deps.js 只有一个方法 parseDependencies,该方法能够根据一坨js代码,解析出这些js代码中依赖的其余模块,也就是require的那些模块。主要是为了解决,在define方法中,第二个参数没有指明依赖的模块,但又在回调方法中使用require了其余模块的状况。
config.js 实现了 seajs.config 方法,用于保存一些配置信息,模块别名、url路由规则、调试开关;保存完后,会emit一个config事件。
module.js 是最核心的一个文件,全部模块化相关的代码都在这个文件中,好比 define方法、require,use,模块的加载、初始化、解析、执行。
核心代码
gloable.define 最终调用的是Module.define ,该方法会从cachedMods 中根据id找到相应的Module对象(若是不存在,则建立一个并保存到cachedMods中),使用define参数中的 deps(依赖模块id)、factory(第三个参数)来初始化相应的本模块。若是deps参数为空,则define方法会调用parseDependencies(util-deps.js),解析factory中经过require引用的其余模块,并把解析的结果赋值给本模块。
define 涉及到的方法主要有 Module.save、Module.get,调用层次以下:
define
|-- Module.save : 建立 module , 并缓存在 cachedMods 中,而且,若是新建立的module 状态小于SAVED,则设置为 SAVED
`-- Module.get : 根据 id 从 cachedMods 中获取 Module 对象,若是没有则建立 Module,并缓存在 cachedMods 中。javascript
seajs.use(Module.use) 是模块的入口,该方法会从服务端加载直接依赖的模块,以及依赖模块的依赖模块...也就是全部间接依赖的模块;当全部模块加载完以后,浏览器会自动执行这些模块的define方法,在define中会建立这些模块对应的Module对象,并保存到cachedMods中;全部模块触发onload事件(发生在define执行以后)以后,seajs会执行直接依赖模块的exec方法;exec方法会构造require、exports、module,做为参数传递给本模块factory方法。
use 涉及到的方法主要有,load resolve get pass fetch,调用层次以下:
seajs.use
|-- Module.use
|-- Module.get : new一个Module,并把new的Module缓存到cachedMods变量中。状态为 -1
|-- Module.prototype.load: 先把当前模块的依赖模块存储到cachedMod中,而后依次从服务端加载(Fetch)依赖模块,
加载完而且浏览器执行完define方法后,onload事件中会调用依赖模块的load方法,从而达到递归加载直接和间接依赖的目的。
|-- Module.prototype.resolve :处理依赖模块的url,把依赖模块的id转换为url并返回。
|-- Module.get :new依赖模块
|-- Module.prototype.pass: 把入口模块(通常是经过seajs.using)传递给依赖模块,最终传递给最后一个被依赖的模块;当最后一个被依赖的模块加载完以后,会经过onload方法,调用入口模块的callback,从而执行Module.exec方法。
|-- Module.prototype.fetch : 遍历当前模块所依赖的模块,把依赖模块从服务端加载出来,加载完后,浏览器会当即执行模块的define方法,此时依赖模块的状态为SAVED。
|-- seajs.request :用webWorker或建立script标签的方式加载javascript脚本。
|-- gloable.define 脚本加载完后,由浏览器调用。
`--Module.prototype.exec : 在onload方法中,会执行此方法。 修改状态为 EXECUTING,构造require、exports、module,执行factory方法(difine中的回调方法)。
require 方法会根据 id 获取module对象,而后调用 该module的 exec 方法,调用层次:
require : 根据 id 获取module对象,而后调用 该module的 exec 方法
|-- Module.get 从cachedMod中获取 或建立对象。
`-- Module.prototype.exec : 修改状态为 EXECUTING,构造require、exports、module,执行factory方法(difine中的回调方法)。
在执行以上方法的时候,module会有一个status属性标记着当前模块的状态,一个模块的生命周期以下:
var STATUS = Module.STATUS = {
// 1 - 正在执行 fetch 方法
FETCHING: 1,
// 2 - 执行完了 saved 方法
SAVED: 2,
// 3 - 正在执行完load方法
LOADING: 3,
// 4 - 已经load完
LOADED: 4,
// 5 - 正在执行exec方法
EXECUTING: 5,
// 6 - exec方法执行完毕
EXECUTED: 6,
// 7 - 出现错误,通常是404。
ERROR: 7
}
define、require、exec等方法在执行时,都会触发(emit)相应的事件,以便经过插件的方式实现除seajs核心之外的功能,如 seajs-combo 等。