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. 【小白的CFD之旅】06 流体力学基础

    从黄师姐那里了解到要学习CFD的话,需要先补充流体力学.数学以及计算机方面的常识,小白就一阵头大.想起当初自己已经把牛皮吹出去了,现在都不知道怎么收场,一个月入不了门多丢人.不过头大归头大,小白还是老 ...

  2. python爬虫学习 —— 总目录

    开篇 作为一个C党,接触python之后学习了爬虫. 和AC算法题的快感类似,从网络上爬取各种数据也很有意思. 准备写一系列文章,整理一下学习历程,也给后来者提供一点便利. 我是目录 听说你叫爬虫 - ...

  3. HTML中的div,section,article的区别

    刚开始看到标签的就有些疑惑,觉得为什么有那么多相同用途的标签,多方查询资料细细比较之后才发现原来各有千秋,结合自己的想法总结如下: div在HTML早期版本就支持了,section和article是H ...

  4. SPOJ GSS1 Can you answer these queries I[线段树]

    Description You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A q ...

  5. [No000072]Windows环境变量列表

    环境变量是目录的可以直接在绝对路径中引用,所有值均可在CMD下用 echo 命令显示以查看. 最常用的有—— %APPDATA% %HOMEPATH% %ProgramFiles% %SYSTEMRO ...

  6. 使用 v-cloak 防止页面加载时出现 vuejs 的变量名

    使用 vuejs 做了一个简单的功能页面,逻辑是,页面加载后获取当前的经纬度,然后通过 ajax 从后台拉取附近的小区列表.但是 bug 出现了,在显示小区列表之前,会闪现小区名对应的 vuejs 变 ...

  7. bash 脚本编程 利用 “=” 赋值时,左右不能留空格

    对脚本变量用“=”赋值时, "=" 左右不能留有空格,否则会提示错误. 比如以下例子: #!/bin/bash BEGIN_TIME = `date +%H:%M:%S` ./a. ...

  8. 关于SIGSEGV错误及处理方法(转)

    转自:http://blog.csdn.net/brace/article/details/1102422   今天编程遇到了SIGSEGV错误,比较困惑,所以找了些资料,总结一下: (1)官方说法是 ...

  9. Stack Overflow: The Architecture - 2016 Edition

    To get an idea of what all of this stuff “does,” let me start off with an update on the average day ...

  10. ActiveX控件之ActiveXObject is not defined

    ActiveX控件方便用户在网页中插入各种效果,但是并不是所有浏览器都支持该控件. ActiveX是微软独有的,只有基于IE内核的浏览器才能使用. 当出现如上错误,可以将通过该控件创建的对象定义为本地 ...