自己边读变加了一些注释,理解了一下seajs3.0.0工作的流程。正则没有一个个去理解,插件模块也没看, 以后有时间了可以补充完整~

事件系统中事件队列的获取&定义方法

var list = events[name] || (events[name] = [])

以前自己写都是

if(!events[name]){
events[name]=[];
}
var list=events[name];

加载模块文件的方法

webworker环境下加载模块文件

获取seajs的加载路径:

var stack;
try {
var up = new Error();
throw up;
} catch (e) {
// IE won't set Error.stack until thrown
stack = e.stack.split('\n');
}
//  at Error (native) <- Here's your problem
// at http://localhost:8000/_site/dist/sea.js:2:4334 <- What we want
// at http://localhost:8000/_site/dist/sea.js:2:8386
// at http://localhost:8000/_site/tests/specs/web-worker/worker.js:3:1

根据url加载模块,使用的是webworker全局环境下的importScripts方法

function requestFromWebWorker(url, callback, charset) {
// Load with importScripts
var error;
try {
importScripts(url);
} catch (e) {
error = e;
}
callback(error);
}
// For Developers
seajs.request = requestFromWebWorker;

浏览器js线程中异步加载模块文件

  // 通过script标签加载脚本
function request(url, callback, charset) {
var node = doc.createElement("script") if (charset) {
var cs = isFunction(charset) ? charset(url) : charset
if (cs) {
node.charset = cs
}
} addOnload(node, callback, url) 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
}
//添加加载完成之后的处理,包括报错、清除对象、移除dom等操作
function addOnload(node, callback, url) {
var supportOnload = "onload" in node if (supportOnload) {
node.onload = onload
node.onerror = function() {
emit("error", { uri: url, node: node })
onload(true)
}
}
else {
node.onreadystatechange = function() {
if (/loaded|complete/.test(node.readyState)) {
onload()
}
}
} function onload(error) {
// 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 (!data.debug) {
head.removeChild(node)
} // Dereference the node
node = null callback(error)
}
} // For Developers
seajs.request = request

核心模块类 Module

Module类定义

//定义module类
function Module(uri, deps) {
//模块路径
this.uri = uri
//这是一个字符串数组,内容是该模块依赖模块的id列表
this.dependencies = deps || []
//这是该模块所依赖的模块的一个map key为id,value为Module对象
this.deps = {} // Ref the dependence modules
//模块状态
this.status = 0
//用来辅助load的一个数组,load完成后会删掉
this._entry = []
}

模块缓存

var cachedMods = seajs.cache = {}

原型方法列表

// 获取模块中依赖模块的uri数组
Module.prototype.resolve 
// 一个辅助模块load过程的函数
Module.prototype.pass
//加载并执行模块
Module.prototype.load
//模块加载完成后触发
Module.prototype.onload
//模块404时候触发
Module.prototype.error
//执行模块
Module.prototype.exec
//用来获取模块文件,每调用一次给一个缓存对象设置一个key value,value是一个函数,调用即开始加载文件

函数方法列表

//通过id获取文件uri
Module.resolve = function(id, refUri)
//用来定义一个模块
Module.define= function (id, deps, factory)
//将id、依赖、 factory 、模块加载状态设置给缓存中的模块
Module.save= function(uri, meta)
////根据uri,从缓存中获取模块,或者以接收的uri和deps参数创建一个模块返回,并加入缓存
Module.get=function(uri, deps)
//用来加载并运行模块
Module.use = function(ids, callback, uri)

暴露出来的API

 // Public API

 seajs.use = function(ids, callback) {
//use 实际上创建了一个id为data.cwd + "_use_" + cid()的模块,这个模块的依赖模块是待启动的模块
Module.use(ids, callback, data.cwd + "_use_" + cid())
return seajs
} Module.define.cmd = {}
global.define = Module.define // For Developers seajs.Module = Module
data.fetchedList = fetchedList
data.cid = cid seajs.require = function(id) {
var mod = Module.get(Module.resolve(id))
if (mod.status < STATUS.EXECUTING) {
mod.onload()
mod.exec()
}
return mod.exports
} /**
* config.js - The configuration for the loader
*/ // The root path to use for id2uri parsing
data.base = loaderDir // The loader directory
data.dir = loaderDir // The loader's full path
data.loader = loaderPath // The current working directory
data.cwd = cwd // The charset for requesting files
data.charset = "utf-8" // data.alias - An object containing shorthands of module id
// data.paths - An object containing path shorthands in module id
// data.vars - The {xxx} variables in module id
// data.map - An array containing rules to map module uri
// data.debug - Debug mode. The default value is false seajs.config = function(configData) { for (var key in configData) {
var curr = configData[key]
var prev = data[key] // Merge object config such as alias, vars
if (prev && isObject(prev)) {
for (var k in curr) {
prev[k] = curr[k]
}
}
else {
// Concat array config such as map
if (isArray(prev)) {
curr = prev.concat(curr)
}
// Make sure that `data.base` is an absolute path
else if (key === "base") {
// Make sure end with "/"
if (curr.slice(-1) !== "/") {
curr += "/"
}
curr = addBase(curr)
} // Set config
data[key] = curr
}
} emit("config", configData)
return seajs
}

常用API和特性的理解

seajs.use原理

seajs.use ("index.js")=Module.use(["index.js"],undefined,"http://xxxx.com/xxx/xxx/_use_i")
① 通过Module.get创建模块mod,
② 通过Module.prototype.load加载mod模块,为mode定义history、remain、callback属性,设置_entry
 
Module.prototype.load中:
①通过Module.prototype.resolve函数获取模块的依赖模块uri数组。
Module.prototype.resolve会遍历mod对象的dependencies中的模块id,挨个通过Module.resolve函数获取其uri,最后返回数组
② 遍历上一步获取的uri,通过Module.get获取模块,给mod的deps属性设置值,key为依赖模块id,value为依赖模块对象
④ 通过Module.prototype.fetch 获取所有依赖模块的模块文件
⑤ 对子模块重复以上过程
⑥mod模块加载完成后,执行onload回调,在onload中执行Module.use的回调callback,在callback中执行Module.prototype.exec来运行该模块

require、exports、module的注入&懒执行

Module.use的回调callback中会运行模块的Module.prototype.exec ,返回module的exports对象。
这个过程中,若factory是function,则调用factory,参数传入require,mod.exports,mod。
require:
  function require(id) {
var m = mod.deps[id] || Module.get(require.resolve(id))
if (m.status == STATUS.ERROR) {
throw new Error('module was broken: ' + m.uri);
}
return m.exec()
}
注入的过程:
// Exec factory
var factory = mod.factory
//执行factory的代码,这个时候才执行,懒执行
var exports = isFunction(factory) ?
factory(require, mod.exports = {}, mod) :
factory
可以看到,require的作用是从缓存中获取模块,然后exec。也就是说,只有require函数被执行的时候,模块代码才会被执行。这就是seajs的as lazy as posible ,懒执行。

global.define

define函数的作用是从参数中获取 id 、dependences 、  factory,然后从模块缓存中获取模块对象,或者创建新模块加入缓存,将id、dependences、factory设置给模块。
贴上关键代码:
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
}
}
// 从function字符串中获取依赖模块id
if (!isArray(deps) && isFunction(factory)) {
deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString())
}
var meta = {
id: id,
uri: Module.resolve(id),
deps: deps,
factory: factory
}
meta.uri ? Module.save(meta.uri, meta) :
// Save information for "saving" work in the script onload event
anonymousMeta = meta
}

(为了方便理解,源码中一些辅助代码被我剪切掉了)

 最后附上我写过注释的源码

seajs3.0.0源码分析记录的更多相关文章

  1. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  2. Spark2.1.0之源码分析——事件总线

    阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析——事件总线>.<Spark2.1.0事件总线分析——ListenerBus的继承体系>及<Spark2. ...

  3. jQuery 2.0.3 源码分析 事件体系结构

    那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...

  4. jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...

  5. jQuery 2.0.3 源码分析 Deferred概念

    JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...

  6. jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

    事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...

  7. jQuery 2.0.3 源码分析 Deferrred概念

    转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...

  8. jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...

  9. jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...

随机推荐

  1. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  2. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  3. 一个表缺失索引发的CPU资源瓶颈案例

    背景 近几日,公司的应用团队反应业务系统突然变慢了,之前是一直比较正常.后与业务部门沟通了解详情,得知最近生意比较好,同时也在做大的促销活动,使得业务数据处理的量出现较大的增长,最终系统在处理时出现瓶 ...

  4. 独立开发 一个社交 APP 的架构分享 (已实现)

    (本博客为原创:http://www.cnblogs.com/linguanh/)   My BananaCloud Android Application 前言:  这算是我的第一个 完完全全 由自 ...

  5. AFNetworking 3.0 源码解读(八)之 AFImageDownloader

    AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...

  6. 消息队列性能对比——ActiveMQ、RabbitMQ与ZeroMQ(译文)

    Dissecting Message Queues 概述: 我花了一些时间解剖各种库执行分布式消息.在这个分析中,我看了几个不同的方面,包括API特性,易于部署和维护,以及性能质量..消息队列已经被分 ...

  7. iOS微信里打开app,Universal Links

    这两天在弄分享,从第三方应用或者浏览器打开自己app的东西 传统的方式是通过URL Scheme的方式,但是iOS9以后又出了新的更完美的方式Universal Links. 传统的URL Schem ...

  8. Log4net - 规则简介

    参考页面: http://www.yuanjiaocheng.net/CSharp/csharprumenshili.html http://www.yuanjiaocheng.net/entity/ ...

  9. cocos2dx调用浏览器打开网址

    安卓端cocos2dx/platform/android路径下CCApplication.h: virtual void openURL(const char* pszUrl); CCApplicat ...

  10. 解决:SharePoint当中的STP网站列表模板没有办法导出到其它语言环境中使用

    首在在你的英文版本上,导出列表或是网站的模板,这个文件可能是这样滴:template.stp 把这个文件 template.stp 命名为 template.cab 解压 这个 *.cab 文件 在解 ...