seaJS 模块加载过程分析
先看一个seajs的官方example, 以下以seajs.use('main')为例, 解析加载mod main的过程
//app.html
seajs.use("main"); //main.js
define(function(require) {
var Spinning = require('./spinning');
var s = new Spinning('#container');
s.render();
}); //spinning.js
define(function(require, exports, module) {
var $ = require('jquery'); function Spinning(container) {
this.container = $(container);
this.icons = this.container.children();
this.spinnings = [];
}
module.exports = Spinning;
function random(x) { return Math.random() * x };
});
module在加载过程中的几种状态
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
}
每个mod在加载过程中会维护一些自身的重要属性,如
dependencies: 模块的直接依赖
_remain: 默认为当前mod的dependencies的length,用来加锁,只有当自身直接依赖的所有模块都加载完毕,即状态为LOADED的时候, _remain变为0,此时触发当前mod的onload, 通知直接依赖它的模块。
_waiting: 存储直接依赖它的模块表,这个属性,使得模块之间建立起依赖关系链, 对模块加载完成后的通知机制非常重要。
factory: 为define(id, deps, factory) module定义中的参数
callback: 为使用use时生成的module的特有属性, 这个在唤醒机制的时候比较有用
图解seajs.use('main'), 模块的加载过程如下:

执行过程分析:
使用use调用,会自动构建一个module,id为当前文档中use的次数和use进行的拼接, 如_use_0, 一般它会成为依赖链的源头。
模块加载完成后,先触发JS文件内容执行,此时define函数被执行, 完成该module的两个重要属性dependencies & _waiting的初始化。
模块加载完成后,触发onload事件,如当前模块有dependencies,进入并行加载流程。 或者当发现当前模块的dependencies全部加载完毕或者为空时, 根据_waiting来逐级唤醒,执行module的callback,由于只有使用use调用的时候才会为mod添加callback属性,所以这个过程一般会向上索引到依赖链的源头,在上述代码实例中,这个依赖链大概可以描述如下:

执行use函数创建的module 会给添加一个自定义的callback函数, 因为通常作为依赖链的源头, 需要从它开始计算和它有直接关系的dependencies的exports, 计算过程相当于将依赖的module的factory函数依次执行一遍,如在执行中发现代码中有requrie的情况,如main.js 文件中的 var Spinning = require('./spinning'),再执行一下require 的module。
代码如下:
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
}
计算好exports后, 会用exports作为参数在调用当前module原来的callback, module执行完毕。
和requirejs的最大区别
最大的区别就是seajs是模块加载和模块执行分离, 而requirejs是加载完毕后,会提前计算好module的exports; 在factory 的FunctionBody中的require('xxx'), 只是直接获取计算好的module xxx 的 exports 而已。
总结
看完seajs和requirejs的模块加载过程, 对脚本动态加载有了更深入的理解, 在代码的设计上, 通过使用_waiting来维护一个依赖关系链,执行的时候依次向上回溯,找到有callback属性的mod开始执行, 就让本来复杂的依赖关系管理变得简单, 将代码加载和执行分离有好处也会坏处,这个网上也有很多讨论,就不展开描述, 总之还是觉得值得一看吧。
end
seaJS 模块加载过程分析的更多相关文章
- seajs模块加载与执行原理小记
本文仅讨论具名模块的情况,即通过spm打包出来的模块. 想起ID与路径统一原则,详见https://github.com/seajs/seajs/issues/930 今天又研究了下seajs源码,源 ...
- Insmod模块加载过程分析
一.背景 a) 在进行JZ2440的一个小demo开发的时候,使用自己编译的内核(3.4.2)及lcd模块进行加载时,insmod会提示加载失败因为内核版本不匹配(提示当前内核版本为空),并且显示模块 ...
- seajs实现JavaScript 的 模块开发及按模块加载
seajs实现了JavaScript 的 模块开发及按模块加载.用来解决繁琐的js命名冲突,文件依赖等问题,其主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载. 官方文档:http:/ ...
- 转: javascript模块加载框架seajs详解
javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...
- javascript模块加载框架seajs详解
SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加载).SeaJS可以和jQuery完美集成,使用 ...
- SeaJS:一个适用于 Web 浏览器端的模块加载器
什么是SeaJS?SeaJS是一款适用于Web浏览器端的模块加载器,它同时又与Node兼容.在SeaJS的世界里,一个文件就是一个模块,所有模块都遵循CMD(Common Module Definit ...
- 使用RequireJS并实现一个自己的模块加载器 (一)
RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...
- 【模块化编程】理解requireJS-实现一个简单的模块加载器
在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ..... ...
- 第三课:sea.js模块加载原理
模块加载,其实就是把js分成很多个模块,便于开发和维护.因此加载很多js模块的时候,需要动态的加载,以便提高用户体验. 在介绍模块加载库之前,先介绍一个方法. 动态加载js方法: function l ...
随机推荐
- 2、各种折腾,安装交叉环境的gcc和qt,测试c++和qt程序
本博文仅作本人操作过程的记录,留作备忘.自强不息 QQ1222698 1.安装gcc和qt 把光盘里带的gcc-4.4.4-glibc-2.11.1-multilib-1.0_EasyARM-iMX2 ...
- 从源码(编译)安装golang 二
h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...
- MySQL 的调节和优化的提示
MySQL 服务器硬件和操作系统调节: 1. 拥有足够的物理内存来把整个InnoDB文件加载到内存中——在内存中访问文件时的速度要比在硬盘中访问时快的多.2. 不惜一切代价避免使用Swap交换分区 – ...
- CSS 去掉inline-block间隙的几种方法
最近做移动端页面时,经常会用到inline-block元素来布局,但无可避免都会遇到一个问题,就是inline-block元素之间的间隙.这些间隙会导致一些布局上的问题,需要把间隙去掉.对于inlin ...
- S2 深入.NET和C#编程 一: 深入C#.NET框架
深入C#.NET框架 1..NET框架 之一 推荐一个代码管理平台,博客发布平台 git 之前的复习: 学习的网站: git github.com 2.类和对象的关系 Dept de ...
- MySQL 数据类型和约束(外键是重点🙄)
数据类型 1. 数字(默认都是由符号,宽度表示的是显示宽度,与存储无关).tinyint 括号里指定宽度 七位2进制数最大数就是2**7 -1=127 最小是-128 验证: create tabel ...
- Head First设计模式之代理模式
一.定义 定义:为其他对象提供一种代理以控制对这个对象的访问 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口. 二.结构 代理模式一般会有三个角色: 抽象角色(Subject):指代 ...
- 讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self
前言: 在处理完框架内存泄漏的问题后,见上篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)- IOS不为人知的Bug 发现业务代码有一个地方的内存没释放,原因很也简单: ...
- android 删除相册图片并同步到图库
private void deleteImage(String imgPath) { ContentResolver resolver = getContentResolver(); Cursor c ...
- hadoop集群篇--从0到1搭建hadoop集群
一.前述 本来有套好好的集群,可是不知道为什么虚拟机镜像文件损坏,结果导致集群不能用.所以不得不重新搭套集群,借此机会顺便再重新搭套吧,顺便提醒一句大家,自己虚拟机的集群一定要及时做好快照,最好装完每 ...