seajs动态加载js原理_seaJS 模块加载过程分析

本文深入分析了seajs动态加载模块的过程,从seajs.use开始,详细阐述了模块的状态变化、依赖加载、onload事件、模块执行等关键环节。通过对比requirejs,强调了seajs模块加载与执行的分离特性,及其对复杂依赖关系管理的简化作用。
摘要由CSDN通过智能技术生成

先看一个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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值