seajs动态加载js原理_seajs模块加载机制

本文谈论的代码版本是Sea.js 2.3.0,seajs最新的版本是3.0,3.0版本变动较大.

新事物的出现或多或少的是想改变已有的行为方式,解决遇到的问题。关于这段历史,大家可以看看这几篇文章:why-seajs1、why-seajs2

既然已经有很多优秀的文章介绍seajs了,为什么你还要浪费时间呢?

因为脑子不好使,写作的过程使我审视自己的结论,加深了我对事物的理解。

seajs干嘛的

SeaJS是一个遵循CMD规范的JavaScript模块加载框架,用以实现JavaScript的模块化开发及加载机制

注意关键词:CommonJS规范,模块加载,模块化开发

CMD规范全称Common Module Definition,该规范明确了模块的基本书写格式和基本交互规则,即定义了一组模块化开发的接口,具体细节可以参看:CMD 模块定义规范、英文 Common Module Definition

有C++,Java,Python开发经验的同学应该很容易理解模块化开发和模块加载的概念,以java为例,类是java的最小开发单位,一个类可以看做是一个最小模块,一个或多个类可以组成一个package,这种组织代码的方式就是模块化开发

当我们在一个类中import另一个package的内容时,import的内容最终会被包含进当前类中,seajs要做的一个事情就是模拟java中import这种模块加载机制。

与服务器端不同,在浏览器端实现模块加载又有其特点:模块(js文件)需要经过网络传输到达用户浏览器,那么何时将js文件下载到用户本地?性能考虑,提前将用到的js文件下载到用户本地是首选,免去了执行期从服务器请求文件消耗的时间,但同时seajs也提供了异步加载模块的功能。

两个核心

1. 模块的依赖加载

如何从服务器获取js文件,这里就不详细解释了,原理就是append script标签的方式。

模块间的关系无非就是依赖和被依赖的关系,具体到两个模块上,有下图三种关系:

a模块依赖b模块

b模块依赖a模块

a,b模块相互依赖

两个模块间的依赖关系

前两种情况很好理解,对于第三种情况,seajs在3.0版本前是不支持循环依赖的,具体表现为模块加载会中断,a.doSomething()并没有执行,3.0的循环依赖处理和node一样了#1382,下面的代码可以用来测试这个问题。

seajs.use("a" , function(a){

a.doSomething();

});

//a.js

define(function(require, exports , module) {

var b = require("b");

exports.doSomething = function() {

console.log("in a");

};

});

//b.js

define(function(require, exports, module) {

var c = require("c");

exports.doSomething = function() {

console.log("in b");

};

});

//c.js

define(function(require, exports, module) {

var a = require("a");

exports.doSomething = function() {

console.log("in c");

};

});

提炼一下上面描述的内容就是:对于一个模块M,它应该拥有下面的关系

Module{

a: {Module依赖的模块}

b: {依赖Module的模块}

}

在看看seajs中Module的定义:

function Module(uri, deps) {

this.uri = uri

this.dependencies = deps || []//我依赖的模块

this.exports = null

this.status = 0 //我当前的状态

this._waitings = {} //依赖我的模块

this._remain = 0 //我依赖的模块还有多少没有完成加载

}

以一个实际的例子说明一下seajs模块加载的逻辑,如下图:

百度脑图

a依赖b和c

b依赖d

尽管存在上述依赖,但是a,b,c,d模块download到浏览器端的顺序确是a,b,c,d,而不是d,b,c,a,笨想一下后一种执行顺序也是不可能的,因为模块间的依赖只有download到浏览器端seajs才能进行分析。

概括一下整个加载的流程就是:

自顶向下的download,自底向上的反馈准备就绪。

如何做到的呢?

主要是Module中的几个属性发挥的作用,模块被download到浏览器端后,按照CMD规范,define函数会被执行,module.define会分析该模块的依赖,记录到dependencies属性中,define函数执行完毕,绑定在script标签上的onload事件会被触发,进而加载当前模块的依赖模块,也就是执行module.load函数,这是一个循环往复的过程。

假设d模块加载就绪,执行module.load时发现,d模块已无其他依赖,进而执行module.onload, 在module.onload中,通过_waitings属性找到父模块,操作父模块的依赖计数_remain,达到通知父模块的目的。

这是一个完美的反馈系统。

seajs的模块加载可以分为两部分,一部分就是模块加载的过程,另一部分就是模块执行的过程。相比较而言,执行过程就比较简单了,具体可以看源码module.exec这个函数。

下一节分析seajs中模块id的解析原理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值