seajs主要做了2件事

1.定义什么是模块,如何声明模块:id、deps、factory、exports ----define=function(id,deps,factory){return exports}

2.管理模块依赖,解决自动加载。本质其实是个加载器loader:id配上路径规则,factory可以提取依赖模块,递归遍历后自动加载js文件

  核心的代码是util-path.js和module.js

发现seajs已经修订到3.0.0部分实现有改动,有些地方随手写的,也没求证过估计误人子弟不少。忙着review下,后面再补了3、0版的module.js注释贻笑大方

对外接口

seajs.use    执行的入口方法

define       模块定义的初始化方法

seajs.use("a")->Module.get("a")
m.load();执行fetch后加载define调用Module.save跟新deps
m.onload();
m.exec(); 子模块:
deps->Module.get()
fetch/load 通知回调-> 主模板onload 模块状态:
fetch--Module.fetch 拉取模板
saved--Module.saved 拉取模板成功后就save模块(define会提取deps)
loading--m.load()   检测依赖模块是否全部加载
loaded--m.onload()  已加载全部模块后触发
executing/executed-m.exec() seajs.use->mod.onload->mod.callback()->mod.exec(); use是seajs模块入口函数 定义模块:

Module.use(seajs.use)
Module.define(global.define)

核心类:Module

静态方法

Module.use(使用模块load后exec)、Module.preload(获取data.preload参数后执行Module.use)、Module.get(拿到模块,若无则创建)

模块use的执行过程

load     开始加载目标主模块

get      创建主模块

fetch     发送request请求加载模块,加载成功后执行define方法会抽取依赖deps

save      加载成功后会执行define方法,解析依赖的deps。且保持Module.save

load     每次模块加载成功后onRequest回调主模块load判断是否remain==0,调用主模块onload(新版改用Module.prototype.pass)。每次子模板加载成功后,都要通知依赖自己的主模板。更新状态以便判断是否进入onload

onload    全部加载完触发onload,回调callback(seajs.use(id,callback))。只有主模板会有onload执行

exec     执行模块factory(即define(fn)的fn),且将require关键字映射exec上,依次执行所有依赖模块

/*
创建seajs对象,标识版本号。创建data数据对象,data挂在seajs为外部访问
*/
var seajs = global.seajs = {
// The current version of Sea.js being used
version: "@VERSION"
} var data = seajs.data = {} /*
seajs自己的事件触发,on绑定事件,off移除,emit触发
*/
/**
* util-events.js - The minimal events support
*/ var events = data.events = {} // Bind event
seajs.on = function(name, callback) {
var list = events[name] || (events[name] = [])
list.push(callback)
return seajs
} // Remove event. If `callback` is undefined, remove all callbacks for the
// event. If `event` and `callback` are both undefined, remove all callbacks
// for all events
seajs.off = function(name, callback) {
// Remove *all* events
if (!(name || callback)) {
events = data.events = {}
return seajs
} var list = events[name]
if (list) {
if (callback) {
for (var i = list.length - 1; i >= 0; i--) {
if (list[i] === callback) {
list.splice(i, 1)
}
}
}
else {
delete events[name]
}
} return seajs
} // Emit event, firing all bound callbacks. Callbacks are passed the same
// arguments as `emit` is, apart from the event name
var emit = seajs.emit = function(name, data) {
var list = events[name], fn if (list) {
// Copy callback lists to prevent modification
list = list.slice() // Execute event callbacks
while ((fn = list.shift())) {
fn(data)
}
} return seajs
} /*
类型判断和计数函数
*/
/**
* util-lang.js - The minimal language enhancement
*/ function isType(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === "[object " + type + "]"
}
} var isObject = isType("Object")
var isString = isType("String")
var isArray = Array.isArray || isType("Array")
var isFunction = isType("Function") var _cid = 0
function cid() {
return _cid++
} /*
解析路径的处理方法,包括:
dirname 获取路径的目录
realpath 过滤目录中的./ ../
normalize 补全文件名.js后缀
parseAlias 解析配置别名
parsePaths 解析配置中的path
parseVars 解析配置中{}
parseMap 加载路径根据配置匹配规则映射
addBase 通过id,路由规则生成完整绝对路径 id2Uri 根据id获取uri,完整的绝对路径 loaderDir seajs文件路径的获取:根据id=seajsnode查找seajs的dom,获取src后匹配目录
*/
/**
* util-path.js - The utilities for operating path such as id, uri
*/ var DIRNAME_RE = /[^?#]*\// var DOT_RE = /\/\.\//g
var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\// // Extract the directory portion of a path
// dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
// ref: http://jsperf.com/regex-vs-split/2
function dirname(path) {
return path.match(DIRNAME_RE)[0]
} // Canonicalize a path
// realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
function realpath(path) {
// /a/b/./c/./d ==> /a/b/c/d
path = path.replace(DOT_RE, "/") // a/b/c/../../d ==> a/b/../d ==> a/d
while (path.match(DOUBLE_DOT_RE)) {
path = path.replace(DOUBLE_DOT_RE, "/")
} return path
} // Normalize an id
// normalize("path/to/a") ==> "path/to/a.js"
// NOTICE: substring is faster than negative slice and RegExp
function normalize(path) {
var last = path.length - 1 // If the uri ends with `#`, just return it without '#'
if (path.charAt(last) === "#") {
return path.substring(0, last)
} return (path.substring(last - 2) === ".js" ||
path.indexOf("?") > 0 ||
path.substring(last - 3) === ".css") ? path : path + ".js"
} var PATHS_RE = /^([^/:]+)(\/.+)$/
var VARS_RE = /{([^{]+)}/g function parseAlias(id) {
var alias = data.alias
return alias && isString(alias[id]) ? alias[id] : id
} function parsePaths(id) {
var paths = data.paths
var m if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
id = paths[m[1]] + m[2]
} return id
} function parseVars(id) {
var vars = data.vars if (vars && id.indexOf("{") > -1) {
id = id.replace(VARS_RE, function(m, key) {
return isString(vars[key]) ? vars[key] : m
})
} return id
} function parseMap(uri) {
var map = data.map
var ret = uri if (map) {
for (var i = 0, len = map.length; i < len; i++) {
var rule = map[i] ret = isFunction(rule) ?
(rule(uri) || uri) :
uri.replace(rule[0], rule[1]) // Only apply the first matched rule
if (ret !== uri) break
}
} return ret
} var ABSOLUTE_RE = /^\/\/.|:\//
var ROOT_DIR_RE = /^.*?\/\/.*?\// function addBase(id, refUri) {
var ret
var first = id.charAt(0) // Absolute
if (ABSOLUTE_RE.test(id)) {
ret = id
}
// Relative
else if (first === ".") {
ret = realpath((refUri ? dirname(refUri) : data.cwd) + id)
}
// Root
else if (first === "/") {
var m = data.cwd.match(ROOT_DIR_RE)
ret = m ? m[0] + id.substring(1) : id
}
// Top-level
else {
ret = data.base + id
} return ret
} function id2Uri(id, refUri) {
if (!id) return "" id = parseAlias(id)
id = parsePaths(id)
id = parseVars(id)
id = normalize(id) var uri = addBase(id, refUri)
uri = parseMap(uri) return uri
} var doc = document
var loc = location
var cwd = dirname(loc.href)
var scripts = doc.getElementsByTagName("script") // Recommend to add `seajsnode` id for the `sea.js` script element
var loaderScript = doc.getElementById("seajsnode") ||
scripts[scripts.length - 1] // When `sea.js` is inline, set loaderDir to current working directory
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)
} /*
request 动态加载css或者script到head
创建dom原生link、script标签
addOnload 定义浏览器兼容的异步请求回调
getCurrentScript 获取当前资源加载状态为进行中interactive的资源
*/
/**
* util-request.js - The utilities for requesting script and style files
* ref: tests/research/load-js-css/test.html
*/ var head = doc.getElementsByTagName("head")[0] || doc.documentElement
var baseElement = head.getElementsByTagName("base")[0] var IS_CSS_RE = /\.css(?:\?|$)/i
var READY_STATE_RE = /^(?:loaded|complete|undefined)$/ var currentlyAddingScript
var interactiveScript // `onload` event is supported in WebKit < 535.23 and Firefox < 9.0
// ref:
// - https://bugs.webkit.org/show_activity.cgi?id=38995
// - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
// - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = (navigator.userAgent
.replace(/.*AppleWebKit\/(\d+)\..*/, "$1")) * 1 < 536 function request(url, callback, charset) {
var isCSS = IS_CSS_RE.test(url)
var node = doc.createElement(isCSS ? "link" : "script") if (charset) {
var cs = isFunction(charset) ? charset(url) : charset
if (cs) {
node.charset = cs
}
} addOnload(node, callback, isCSS) if (isCSS) {
node.rel = "stylesheet"
node.href = url
}
else {
node.async = true
node.src = url
} // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
// the end of the insert execution, so use `currentlyAddingScript` to
// hold current node, for deriving url in `define` call
currentlyAddingScript = node // ref: #185 & http://dev.jquery.com/ticket/2709
baseElement ?
head.insertBefore(node, baseElement) :
head.appendChild(node) currentlyAddingScript = null
} function addOnload(node, callback, isCSS) {
var missingOnload = isCSS && (isOldWebKit || !("onload" in node)) // for Old WebKit and Old Firefox
if (missingOnload) {
setTimeout(function() {
pollCss(node, callback)
}, 1) // Begin after node insertion
return
} node.onload = node.onerror = node.onreadystatechange = function() {
if (READY_STATE_RE.test(node.readyState)) { // Ensure only run once and handle memory leak in IE
node.onload = node.onerror = node.onreadystatechange = null // Remove the script to reduce memory leak
if (!isCSS && !data.debug) {
head.removeChild(node)
} // Dereference the node
node = null callback()
}
}
} function pollCss(node, callback) {
var sheet = node.sheet
var isLoaded // for WebKit < 536
if (isOldWebKit) {
if (sheet) {
isLoaded = true
}
}
// for Firefox < 9.0
else if (sheet) {
try {
if (sheet.cssRules) {
isLoaded = true
}
} catch (ex) {
// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
isLoaded = true
}
}
} setTimeout(function() {
if (isLoaded) {
// Place callback here to give time for style rendering
callback()
}
else {
pollCss(node, callback)
}
}, 20)
} function getCurrentScript() {
if (currentlyAddingScript) {
return currentlyAddingScript
} // For IE6-9 browsers, the script onload event may not fire right
// after the the script is evaluated. Kris Zyp found that it
// could query the script nodes and the one that is in "interactive"
// mode indicates the current script
// ref: http://goo.gl/JHfFW
if (interactiveScript && interactiveScript.readyState === "interactive") {
return interactiveScript
} var scripts = head.getElementsByTagName("script") for (var i = scripts.length - 1; i >= 0; i--) {
var script = scripts[i]
if (script.readyState === "interactive") {
interactiveScript = script
return interactiveScript
}
}
} /**
* module.js - The core of module loader
*/
//系统全局对象,用来保存全局数据和状态
var cachedMods = seajs.cache = {}
var anonymousMeta var fetchingList = {}
var fetchedList = {}
var callbackList = {} var STATUS = Module.STATUS = {
// 1 - The `module.uri` is being fetched
FETCHING: 1,
// 2 - The meta data has been saved to cachedMods
SAVED: 2,
// 3 - The `module.dependencies` are being loaded
LOADING: 3,
// 4 - The module are ready to execute
LOADED: 4,
// 5 - The module is being executed
EXECUTING: 5,
// 6 - The `module.exports` is available
EXECUTED: 6
} //模块类 id、deps、exports 构造函数
function Module(uri, deps) {
this.uri = uri
this.dependencies = deps || []
this.exports = null
this.status = 0//模块状态
// The number of unloaded dependencies
this._remain = 0
// Who depend on me
this._waitings = {}
} //Module静态方法,Module.use使用模块,Module.preload调用data.preload后回调Module.use
// Load preload modules before all other modules
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()
}
} //入口模块,ids--依赖模块,uri模块id:获取uri模块
// Use function is equal to load a anonymous module
Module.use = function (ids, callback, uri) {
var mod = Module.get(uri, isArray(ids) ? ids : [ids])
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()
} //获取uri模块,若cachedMods中无则创建个新模块
Module.get = function(uri, deps) {
return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
} // seajs.use 入口方法
// 执行过程:A依赖B,B依赖C,seajs.use(A):
/*
fetch 获取A模块的依赖模块(B),B拉取成功后回调A.load(每次依赖的文件onRquest,要跟新自己的状态)
load 遍历A模块依赖的所有模块状态(B/C),直到remain==0,触发onload
onload 当A模块所有依赖都已加载,触发A.callback
exec seajs.use 将执行模块的callback设置为exec,调用A.exec,且A模块中的require映射了exec方法,链式的执行下去。。
*/
seajs.use = function(ids, callback) {
Module.preload(function() {
Module.use(ids, callback, data.cwd + "_use_" + cid())
})
return seajs
} //将依赖的模块通过fetch方法注册,最终执行load请求js,onRequest回调onload
// Load module.dependencies and fire onload when all done
Module.prototype.load = function() {
var mod = this
// If the module is being loaded, just wait it onload call
if (mod.status >= STATUS.LOADING) {
return
}
mod.status = STATUS.LOADING // Emit `load` event for plugins such as combo plugin
var uris = mod.resolve()//获取依赖uris
emit("load", uris) var len = mod._remain = uris.length
var m
// Initialize modules and register waitings 拿到依赖模块注册依赖
for (var i = 0; i < len; i++) {
m = Module.get(uris[i])//获取依赖module
if (m.status < STATUS.LOADED) {//模块未加载
// Maybe duplicate
m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1//记录下有几个模块依赖自己
}
else {
mod._remain--//加载后--
}
} //若所有依赖都加载后,执行onload
if (mod._remain === 0) {
mod.onload()
return
} // Begin parallel loading
var requestCache = {} for (i = 0; i < len; i++) {
m = cachedMods[uris[i]] if (m.status < STATUS.FETCHING) {
m.fetch(requestCache)
}
else if (m.status === STATUS.SAVED) {
m.load()
}
} // Send all requests at last to avoid cache bug in IE6-9. Issues#808
for (var requestUri in requestCache) {
if (requestCache.hasOwnProperty(requestUri)) {
requestCache[requestUri]()
}
}
} //将模块的所有依赖的uri都注册到此模块的requestCache列表中
// Fetch a module
Module.prototype.fetch = function(requestCache) {
var mod = this
var uri = mod.uri mod.status = STATUS.FETCHING // Emit `fetch` event for plugins such as combo plugin
var emitData = { uri: uri }
emit("fetch", emitData)
var requestUri = emitData.requestUri || uri // Empty uri or a non-CMD module
if (!requestUri || fetchedList[requestUri]) {
mod.load()
return
} if (fetchingList[requestUri]) {
callbackList[requestUri].push(mod)
return
}
fetchingList[requestUri] = true
callbackList[requestUri] = [mod] // Emit `request` event for plugins such as text plugin
emit("request", emitData = {
uri: uri,
requestUri: requestUri,
onRequest: onRequest,
charset: data.charset
}) if (!emitData.requested) {
requestCache ?
requestCache[emitData.requestUri] = sendRequest :
sendRequest()
} function sendRequest() {
request(emitData.requestUri, emitData.onRequest, emitData.charset)
} function onRequest() {
delete fetchingList[requestUri]
fetchedList[requestUri] = true // Save meta data of anonymous module
if (anonymousMeta) {
save(uri, anonymousMeta)
anonymousMeta = null
} // Call callbacks
var m, mods = callbackList[requestUri]
delete callbackList[requestUri]
while ((m = mods.shift())) m.load()
}
} //若模块的依赖加载全部加载触发onload回调mod.callback,同时通知所有依赖他的模块
// Call this method when module is loaded
Module.prototype.onload = function() {
var mod = this
mod.status = STATUS.LOADED if (mod.callback) {
mod.callback()
} // Notify waiting modules to fire onload
var waitings = mod._waitings
var uri, m for (uri in waitings) {
if (waitings.hasOwnProperty(uri)) {
m = cachedMods[uri]
m._remain -= waitings[uri]
if (m._remain === 0) {
m.onload()
}
}
} // Reduce memory taken
delete mod._waitings
delete mod._remain
} //解析模块依赖的uris 获取绝对uris
// Resolve module.dependencies
Module.prototype.resolve = function() {
var mod = this
var ids = mod.dependencies
var uris = [] for (var i = 0, len = ids.length; i < len; i++) {
uris[i] = resolve(ids[i], mod.uri)
}
return uris
} //触发resolve事件,返回id2Uri
// Helpers
function resolve(id, refUri) {
// Emit `resolve` event for plugins such as text plugin
var emitData = { id: id, refUri: refUri }
emit("resolve", emitData)
return emitData.uri || id2Uri(emitData.id, refUri)
} //加载完所有依赖的模块后,执行自身模块,同时通过关键字require将依赖的模块顺序执行
// Execute a module
Module.prototype.exec = function () {
var mod = this
// When module is executed, DO NOT execute it again. When module
// is being executed, just return `module.exports` too, for avoiding
// circularly calling
if (mod.status >= STATUS.EXECUTING) {
return mod.exports//模块已执行
}
mod.status = STATUS.EXECUTING
// Create require
var uri = mod.uri //执行require的id
function require(id) {
return cachedMods[require.resolve(id)].exec()
} //获得id模块的绝对路径
require.resolve = function(id) {
return resolve(id, uri)
} //异步请求uri
require.async = function(ids, callback) {
Module.use(ids, callback, uri + "_async_" + cid())
return require
} // Exec factory
var factory = mod.factory var exports = isFunction(factory) ?
factory(require, mod.exports = {}, mod) :
factory if (exports === undefined) {
exports = mod.exports
} // Emit `error` event
if (exports === null && !IS_CSS_RE.test(uri)) {
emit("error", mod)
} // Reduce memory leak
delete mod.factory mod.exports = exports
mod.status = STATUS.EXECUTED // Emit `exec` event
emit("exec", mod) return exports
} //对外的接口 define(function(){...})用于定义模块,初始化模块的基础属性(并不执行模块factory方法)
Module.define.cmd = {}
global.define = Module.define
// Define a module
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
}
} // Parse dependencies according to the module factory code
if (!isArray(deps) && isFunction(factory)) {
deps = parseDependencies(factory.toString())
} var meta = {
id: id,
uri: resolve(id),
deps: deps,
factory: factory
} // Try to derive uri in IE6-9 for anonymous modules
if (!meta.uri && doc.attachEvent) {
var script = getCurrentScript() if (script) {
meta.uri = script.src
} // NOTE: If the id-deriving methods above is failed, then falls back
// to use onload event to get the uri
} // Emit `define` event, used in nocache plugin, seajs node version etc
emit("define", meta) meta.uri ? save(meta.uri, meta) :
// Save information for "saving" work in the script onload event
anonymousMeta = meta
} function save(uri, meta) {
var mod = Module.get(uri) // Do NOT override already saved modules
if (mod.status < STATUS.SAVED) {
mod.id = meta.id || uri
mod.dependencies = meta.deps || []
mod.factory = meta.factory
mod.status = STATUS.SAVED
}
} // For Developers 调试接口
seajs.Module = Module
data.fetchedList = fetchedList
data.cid = cid
//获取模块id
seajs.resolve = id2Uri
//获取模块exports接口
seajs.require = function(id) {
return (cachedMods[resolve(id)] || {}).exports
}

 附commonjs、seajs、requirejs比较

#commonjs 与seajs
* commonjs :node的编写规范,为运行在服务器端的非浏览器环境的模块系统定义。模块间的依赖通过require同步加载,导致浏览器端无法采用这种设计
* seajs:将commonjs的规范扩展后,可以在浏览器环境下运行的模式系统设计规范。通过define(function(){});
 
#requirejs 与seajs
requirejs是amd规范,seajs是cmd规范
 
 
补充Sea.js 3.0.0 module.js注释
function Module(uri, deps) {
this.uri = uri
this.dependencies = deps || []
this.status = 0 this._entry = []
} //获取模板的全路径
// Resolve id to uri
Module.resolve = function(id, refUri) {
// Emit `resolve` event for plugins such as text plugin
var emitData = { id: id, refUri: refUri }
emit("resolve", emitData) return emitData.uri || seajs.resolve(emitData.id, refUri)
} //定义一个模块mod
// Define a module
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
}
} // Parse dependencies according to the module factory code
if (!isArray(deps) && isFunction(factory)) {
deps = parseDependencies(factory.toString())
} var meta = {
id: id,
uri: Module.resolve(id),
deps: deps,
factory: factory
} // Try to derive uri in IE6-9 for anonymous modules
if (!meta.uri && doc.attachEvent) {
var script = getCurrentScript() if (script) {
meta.uri = script.src
} // NOTE: If the id-deriving methods above is failed, then falls back
// to use onload event to get the uri
} // Emit `define` event, used in nocache plugin, seajs node version etc
emit("define", meta) meta.uri ? Module.save(meta.uri, meta) :
// Save information for "saving" work in the script onload event
anonymousMeta = meta
} //生成mod,赋值一些基本属性
// Save meta data to cachedMods
Module.save = function(uri, meta) {
var mod = Module.get(uri) // Do NOT override already saved modules
if (mod.status < STATUS.SAVED) {
mod.id = meta.id || uri
mod.dependencies = meta.deps || []
mod.factory = meta.factory
mod.status = STATUS.SAVED emit("save", mod)
}
} //创建mod,缓存到cachedMods
// Get an existed module or create a new one
Module.get = function(uri, deps) {
return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
} //load执行一个匿名module
// Use function is equal to load a anonymous module
Module.use = function (ids, callback, uri) {
var mod = Module.get(uri, isArray(ids) ? ids : [ids]) mod._entry.push(mod)
mod.history = {}
mod.remain = 1 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
delete mod.history
delete mod.remain
delete mod._entry
} mod.load()
}
/*************************************************************
对外只暴露
Module.use(seajs.use)
Module.define(global.define)
************************************************************/ //获得mod的所有dep的uri
// Resolve module.dependencies
Module.prototype.resolve = function() {
var mod = this
var ids = mod.dependencies
var uris = [] for (var i = 0, len = ids.length; i < len; i++) {
uris[i] = Module.resolve(ids[i], mod.uri)
}
return uris
} //递归遍历自身依赖的子模板,判断全部加载完毕后执行onload
// Load module.dependencies and fire onload when all done
Module.prototype.load = function() {
var mod = this // If the module is being loaded, just wait it onload call
if (mod.status >= STATUS.LOADING) {
return
} mod.status = STATUS.LOADING // Emit `load` event for plugins such as combo plugin
var uris = mod.resolve()
emit("load", uris) //将依赖uri遍历,判断是否全部加载
// Pass entry to it's dependencies
mod.pass(uris) //如果全部加载,执行onload
// If module has entries not be passed, call onload
if (mod._entry.length) {
mod.onload()
return
} // Begin parallel loading
var requestCache = {}
var m
//对依赖的模板执行拉取,拉取后执行load递归子依赖
for (var i = 0, len = uris.length; i < len; i++) {
m = cachedMods[uris[i]] if (m.status < STATUS.FETCHING) {
m.fetch(requestCache)
}
//若模板已经拉取过,跳过拉取执行load
else if (m.status === STATUS.SAVED) {
m.load()
}
} // Send all requests at last to avoid cache bug in IE6-9. Issues#808
for (var requestUri in requestCache) {
if (requestCache.hasOwnProperty(requestUri)) {
requestCache[requestUri]()
}
}
} //将依赖uri遍历,判断是否全部加载
Module.prototype.pass = function(uris) {
var mod = this uris = uris || mod.resolve()
var len = uris.length //use时mod._entry.push(mod)
for (var i = 0; i < mod._entry.length; i++) {
var entry = mod._entry[i]
var count = 0
for (var j = 0; j < len; j++) {
var m = Module.get(uris[j])
// If the module is unload and unused in the entry, pass entry to it
if (m.status < STATUS.LOADED && !entry.history.hasOwnProperty(m.uri)) {
entry.history[m.uri] = true
count++
m._entry.push(entry)
if(m.status === STATUS.LOADING) {
m.pass()
}
}
}
// If has passed the entry to it's dependencies, modify the entry's count and del it in the module
if (count > 0) {
entry.remain += count - 1
mod._entry.shift()
i--
}
}
} //拉取模板,成功拉取后save并通知依赖自己的模板更新load
// Fetch a module
Module.prototype.fetch = function(requestCache) {
var mod = this
var uri = mod.uri mod.status = STATUS.FETCHING // Emit `fetch` event for plugins such as combo plugin
var emitData = { uri: uri }
emit("fetch", emitData)
var requestUri = emitData.requestUri || uri // Empty uri or a non-CMD module
if (!requestUri || fetchedList.hasOwnProperty(requestUri)) {
mod.load()
return
} if (fetchingList.hasOwnProperty(requestUri)) {
callbackList[requestUri].push(mod)
return
} fetchingList[requestUri] = true
callbackList[requestUri] = [mod] // Emit `request` event for plugins such as text plugin
emit("request", emitData = {
uri: uri,
requestUri: requestUri,
onRequest: onRequest,
charset: data.charset
}) if (!emitData.requested) {
requestCache ?
requestCache[emitData.requestUri] = sendRequest :
sendRequest()
} function sendRequest() {
seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset)
} function onRequest(error) {
delete fetchingList[requestUri]
fetchedList[requestUri] = true // Save meta data of anonymous module
if (anonymousMeta) {
Module.save(uri, anonymousMeta)
anonymousMeta = null
} // Call callbacks
var m, mods = callbackList[requestUri]
delete callbackList[requestUri]
while ((m = mods.shift())) {
// When 404 occurs, the params error will be true
if(error === true) {
m.error()
}
else {
m.load()
}
}
}
} //全部加载完触发onload,回调callback(seajs.use(id,callback))
// Call this method when module is loaded
Module.prototype.onload = function() {
var mod = this
mod.status = STATUS.LOADED for (var i = 0, len = mod._entry.length; i < len; i++) {
var entry = mod._entry[i]
if (--entry.remain === 0) {
entry.callback()
}
} delete mod._entry
} //seajs.use->mod.onload->mod.callback()->mod.exec();
// Execute a module
Module.prototype.exec = function () {
var mod = this // When module is executed, DO NOT execute it again. When module
// is being executed, just return `module.exports` too, for avoiding
// circularly calling
if (mod.status >= STATUS.EXECUTING) {
return mod.exports
} mod.status = STATUS.EXECUTING if (mod._entry && !mod._entry.length) {
delete mod._entry
} //non-cmd module has no property factory and exports
if (!mod.hasOwnProperty('factory')) {
mod.non = true
return
} // Create require
var uri = mod.uri function require(id) {
var m = Module.get(require.resolve(id))
if (m.status == STATUS.ERROR) {
throw new Error('module was broken: ' + m.uri);
}
return m.exec()
} require.resolve = function(id) {
return Module.resolve(id, uri)
} require.async = function(ids, callback) {
Module.use(ids, callback, uri + "_async_" + cid())
return require
} // Exec factory
var factory = mod.factory var exports = isFunction(factory) ?
factory(require, mod.exports = {}, mod) :
factory if (exports === undefined) {
exports = mod.exports
} // Reduce memory leak
delete mod.factory mod.exports = exports
mod.status = STATUS.EXECUTED // Emit `exec` event
emit("exec", mod) return exports
}
 
 

seajs源码分析的更多相关文章

  1. JavaScript 模块化及 SeaJs 源码分析

    网页的结构越来越复杂,简直可以看做一个简单APP,如果还像以前那样把所有的代码都放到一个文件里面会有一些问题: 全局变量互相影响 JavaScript文件变大,影响加载速度 结构混乱.很难维护 和后端 ...

  2. 【Seajs源码分析】1. 整体架构

    seajs是一个非常流行的模块开发引擎,目前项目中使用比较多,为了深入了解已经改进seajs我阅读了他的源码,希望对自己的代码生涯能有所启发. 本文说介绍的是指seajs2.3.3版本. 首先seaj ...

  3. seajs源码分析(一)---整体结构以及module.js

    1,seajs的主要内容在module.js内部,最开始包含这么几个东西 var cachedMods = seajs.cache = {} var anonymousMeta var fetchin ...

  4. 【Seajs源码分析】3. 工具方法2

    util-request.js 动态加载模块 /** * util-request.js - The utilities for requesting script and style files * ...

  5. 【Seajs源码分析】2. 工具方法1

    Sea.js: var seajs = global.seajs = { // The current version of Sea.js being used version: "@VER ...

  6. seajs路径问题及源码分析

    seajs如此神奇,究竟是如何做到的呢,想知基原理,方可看其源码~~之前冲忙写下的,可能有点乱哦~~有什么不对的,欢迎拍砖!   如果进入seajs了管理范围,那么路径分为:   1.    /  或 ...

  7. 深入seajs源码系列二

    模块类和状态类 参照上文的demo,我们结合源码分析在简单的API调用的背后,到底使用了什么技巧来实现各个模块的依赖加载以及模块API的导出. 首先定义了一个Module类,对应与一个模块 funct ...

  8. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  9. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

随机推荐

  1. java中设置代理的两种方式

    1 前言 有时候我们的程序中要提供可以使用代理访问网络,代理的方式包括http.https.ftp.socks代理.比如在IE浏览器设置代理. 那我们在我们的java程序中使用代理呢,有如下两种方式. ...

  2. opentsdb basic install

    git clone git://github.com/OpenTSDB/opentsdb.git cd opentsdb ./build.sh env COMPRESSION=NONE HBASE_H ...

  3. hadoop io PART1

    数据正确性检测的技术,通常使用checksum,在数据进行传输前,计算一个checksum值,传输到目标地之后,再根据新的文件计算checksum值,如果不匹配,则说明数据损坏或被改变.只能校验,不提 ...

  4. JS实现类似QQ好友头像hover时显示资料卡的效果

    一.应用场景 鼠标hover弹出div,并且鼠标离开后不能马上隐藏,因为这个div上还有功能入口.比如: 鼠标经过好友列表中的好友头像时显示资料卡的效果 hover时显示二维码 二.实现 用如下这样一 ...

  5. Merge k Sorted Lists

    1. Merge Two Sorted Lists 我们先来看这个 问题: Merge two sorted linked lists and return it as a new list. The ...

  6. loadrnner添加C语言代码的几种方式

    今天有人在群里问,想直接把自己的C语言代码让lr调用,该怎么搞. 这东西说来简单,只是对Loadrunner这工具不熟悉可能才会有这种问题吧.个人理解,一般有三种方法吧,废话不多,直接干货. 1.直接 ...

  7. 06章 映射一对多双向关联关系、以及cascade、inverse属性

    当类与类之间建立了关联,就可以方便的从一个对象导航到另一个对象.或者通过集合导航到一组对象.例如: 对于给定的Emp对象,如果想获得与它关联的Dept对象,只要调用如下方法 Dept dept=emp ...

  8. [No000079]罗辑思维2016.1.2日前的所有每日语音,python3做的网络爬虫

    源码地址:https://github.com/charygao/Download_the_LouJiSiWei 写过很久了,vision1.0里有不少bug,今天重新整理修改了一下,运行了一下,2个 ...

  9. LinkedIn的即时消息:在一台机器上支持几十万条长连接

    最近我们介绍了LinkedIn的即时通信,最后提到了分型指标和读回复.为了实现这些功能,我们需要有办法通过长连接来把数据从服务器端推送到手机或网页客户端,而不是许多当代应用所采取的标准的请求-响应模式 ...

  10. SecureCRT连接虚拟机(ubuntu)配置

    使用SecureCRT连接虚拟机(ubuntu)配置记录   这种配置方法,可以非常方便的操作虚拟机里的Linux系统,且让VMware在后台运行,因为有时候我直接在虚拟机里操作会稍微卡顿,或者切换速 ...