SeaJS代码层分析之一
SeaJS 2.2.1解析
seajs 1.1.0 代码结构和数据结构nuysoft

文件结构
- util-lang.js
- util-event.js
- util-path.js
- util-request.js
- util-deps.js
- module.js - The core of module loader
util-lang
判断数据的类型
- javascript数据类型:
- 原始数据类型:String、Number、Boolean
- 组合数据类型:Object、Array
- 特殊数据类型:Null、Undefined
- 判断的方式
function isType(type) {
return function (obj) {
return {}.toString.call(obj) == "[object " + type + "]"
}
}
var isObject = isType("Object")
- jquery判断方式
type: function( obj ) {
if ( obj == null ) {
return String( obj );
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[ core_toString.call(obj) ] || "object" :
typeof obj;
},
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
- 好处:
- 非对外提供接口,内部使用,精简。
- 封装直接产生是否是该类型的结果,不用频繁在代码里进行判断,好理解。(像jq判断$.type('')==="object")。
- jquery使用seajs的方式的话,那么像isUndefined的函数就不能用isType("Undefined")来生成了. 而且枚举这些类型isXXX太繁琐。
util-events
该事件的实现,不是给开发者用于给自己定义的对象添加事件。而是为了通过事件提供可拓展接口 seajs插件指南,因此实现上比较简单
Sea.js 内部提供了 8 种事件(每个事件触发时会带上相关联的数据):
resolve -- 将 id 解析成为 uri 时触发
load -- 开始加载文件时触发
fetch -- 具体获取某个 uri 时触发
request -- 发送请求时触发
define -- 执行 define 方法时触发
exec -- 执行 module.factory 时触发
config -- 调用 seajs.config 时触发
error -- 加载脚本文件出现 404 或其他错误时触发
- 给对象添加事件,可以参考Emitter事件库的实现
util-path.js
- seajs.resolve 转换id到正常的文件的路径:
function id2Uri(id, refUri) {
if (!id) return ""
id = parseAlias(id) //对id中包含的配置的别名进行处理
id = parsePaths(id) //添加id对应的路径
id = parseVars(id) //添加配置的路径可变变量,详见config
id = normalize(id) //标准化文件路径,添加文件对应的js
var uri = addBase(id, refUri) //添加id对应的地址,如果没有基准地址,则使用seajs文件的加载地址。
/* 基准地址的获取
var cwd = dirname(doc.URL)
var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd)
function getScriptAbsoluteSrc(node) {
return node.hasAttribute ? // non-IE6/7
node.src :
// see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
node.getAttribute("src", 4)
}
*/
uri = parseMap(uri) //url对配置的rule进行逐个map处理
return uri
}
util-request
加载js和css样式文件
- HTML5支持使用docuemnt.head获取head
var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement - 加载js需要考虑获取base标签,因为在ie6非闭合的base存在时,head.appendChild(script)。移除时head.removeChild会出错。 script.parentNode.removeChild( script )就可以使用. 所以存在base,因插入在base之前
- 加载js后需要进行移除,不然大量的js标签讲造成内存泄漏 Dynamic script generation and memory leaks
- 函数:
- function request(url, callback, charset)
- function addOnload(node, callback, isCSS, url)
- function pollCss(node, callback) //css的回调函数是采用setTimeout定期检测加载的样式的属性
- function getCurrentScript() //特别的地方:state interactive, ref: http://goo.gl/JHfFW
util-deps
解析代码里脚本的依赖(require)
module.js
Module模块
- Module接口
- 静态方法
- resolve : define/use/prototype.resolve => seajs.resolve==id2Uri
- 转换id为加载的uri
- define
- 多参数解决方式,判断arguments.length,交换变量的值
- resolve : define/use/prototype.resolve => seajs.resolve==id2Uri
Module.define = function (id, deps, factory) {
var argsLen = arguments.length
// define(factory)
if (argsLen === 1) {
factory = id
id = undefined
}
else if (argsLen === 2) {
factory = deps
// define(deps, factory)
if (isArray(id)) {
deps = id
id = undefined
}
// define(id, factory)
else {
deps = undefined
}
* 正则匹配,解析require依赖的模块:
`deps = parseDependencies(factory.toString())`
* 在 ie 6-9 浏览器中可以拿到当前运行js的路径 但是在标准浏览器中 这不可行 所以暂时先把 元信息赋值给anonymousModuleMeta = meta, 但由于是匿名模块,该模块的 uri 信息,需要等到 onload 触发后才能获取到。:
meta.uri ? Module.save(meta.uri, meta) :
// Save information for "saving" work in the script onload event
anonymousMeta = meta
* save
* get
* *获取一个已经存在在缓存里的模块或者创建一个新模块*
* preload 预加载. 用于seajs配置中进行预加载的模块
Module.preload = function (callback) {
var preloadMods = data.preload //从配置的全景数据里面取出预加载模块
var len = preloadMods.length
if (len) {
Module.use(preloadMods, function () {
// Remove the loaded preload modules
preloadMods.splice(0, len) //这里好像不可以去除预加载模块啊
// Allow preload modules to add new preload modules
Module.preload(callback)
}, data.cwd + "_preload_" + cid())
}
else {
callback()
}
}
* use:
Module.use = function (ids, callback, uri) {
var mod = Module.get(uri, isArray(ids) ? ids : [ids])
//从缓存模块里面取依赖的模块并执行,取出里面的exports,并传给实际的回调
mod.callback = function () {
var exports = []
var uris = mod.resolve()
for (var i = 0, len = uris.length; i < len; i++) {
exports[i] = cachedMods[uris[i]].exec()
}
if (callback) {
callback.apply(global, exports)
}
delete mod.callback
}
mod.load()
}
- 原型上的方法
- Module: 不具体分散到外部进行实例化,而采用Module自己的静态方法get创建,可以从中获取或缓存模块,并只对状态进行存储
function Module(uri, deps) {
this.uri = uri //保存id引用以便之后进行加载
this.dependencies = deps || [] //依赖模块引用
this.exports = null //导出接口对象引用
this.status = 0 //当前模块的状态
// Who depends on me
this._waitings = {} //依赖于我的模块
// The number of unloaded dependencies
this._remain = 0 //未加载模块数
// this.id is set when saving
// this.dependencies is set when saving
// this.factory is set when saving
// this.exports is set when compiling
}
Module.get = function (uri, deps) {
return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
}
* resolve: 返回 分解出的依赖的模块的地址
* load
* seajs.use => module=>preload=>use=>load
*
* onload
* fetch
* exec
- define
- 多参数解决方式,判断arguments.length,交换变量的值
Module.define = function (id, deps, factory) {
var argsLen = arguments.length
// define(factory)
if (argsLen === 1) {
factory = id
id = undefined
}
else if (argsLen === 2) {
factory = deps
// define(deps, factory)
if (isArray(id)) {
deps = id
id = undefined
}
// define(id, factory)
else {
deps = undefined
}
浙公网安备 33010602011771号