seajs 源码阅读笔记

代码概览

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 中。

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 等。

时间: 2024-12-26 04:29:29

seajs 源码阅读笔记的相关文章

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

seajs源码阅读

乘着周日有点时间,阅读一下玉伯大神的源码. seajs的源码写得真的很好,很是佩服,工整美观不愧是大神,造福百姓. 说起seajs不得不说,AMD和CMD的区别.  CMD 推崇依赖就近,AMD 推崇依赖前置. 事实上我对他们的区别没啥兴趣.关键是requirejs没明显的BUG,seajs明显没BUG. 两者最大区别请看这里:http://www.cnblogs.com/gyjWEB/p/4543945.html 好了,扯正题. 可以先看看别人写的源码解析:https://segmentfau

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *