设计联盟

Sea.js的运行原理及入门讲解

发布日期:2016-01-13 09:32:24   浏览量:

pixelegos通过同步的require函数获取tool、canvas和menu,后三者同样通过require来执行各自的依赖模块,于是通过这样一个递归的过程,pixelegos就执行完毕了。

打包模块的加载过程:

打包的方式有三种,self,relative和all。
self,只是自己做了transport
relative,将多有相对路径的模块transport,concat
all,包括相对路径模块和库模块(即在seajs-modules文件夹中的),transport,concat

加载方式(以压缩的pixelegos.js为例)
  (1)在use时,定义一个匿名的use_模块,依赖于/dist/pixelegos模块,匿名的use_模块load依赖,开始加载pixelegos.js模块;
  (2)pixelegos.js加载执行,所有打包在里面的模块被define;
  (3)pixelegos.js的onload回调执行,调用/dist/pixelegos模块的load,加载其依赖模块,但依赖的模块都加载好了;
  (4)通知匿名的use_加载完成,开始执行期。

3.Sea.js的实现

module.js是Sea.js的核心,Sea.js中为模块定义了六种状态:

FETCHING:开始从服务端加载模块
SAVED:模块加载完成
LOADING:加载依赖模块中
LOADED:依赖模块加载完成
EXECUTING:模块执行中
EXECUTED:模块执行完成

Sea.use调用Module.use构造一个没有factory的模块,该模块即为这个运行期的根节点

// Use function is equal to load a anonymous module
Module.use = function (ids, callback, uri) {
    var mod = Module.get(uri, isArray(ids) ? ids: [ids])

    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
    }

    mod.load()
}

模块构造完成,则调用mod.load()来同步其子模块;直接跳过fetching这一步;mod.callback也是Sea.js不纯粹的一点,在模块加载完成后,会调用这个callback。
在load方法中,获取子模块,加载子模块,在子模块加载完成后,会触发mod.onload():

// Load module.dependencies and fire onload when all done
Module.prototype.load = function () {
    var mod = this

    // If the module is being loaded, just wait it onload call
    if (mod.status >= STATUS.LOADING) {
        return
    }

    mod.status = STATUS.LOADING

    // Emit `load` event for plugins such as combo plugin
    var uris = mod.resolve()
    emit("load", uris)

    var len = mod._remain = uris.length
    var m

    // Initialize modules and register waitings
    for (var i = 0; i < len; i++) {
        m = Module.get(uris[i])

        if (m.status < STATUS.LOADED) {
            // Maybe duplicate
            m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1
        }
        else {
            mod._remain--
        }
    }

    if (mod._remain === 0) {
        mod.onload()
        return
    }

    // Begin parallel loading
    var requestCache = {}

    for (i = 0; i < len; i++) {
        m = cachedMods[uris[i]]

        if (m.status < STATUS.FETCHING) {
            m.fetch(requestCache)
        }
        else if (m.status === STATUS.SAVED) {
            m.load()
        }
    }

    // Send all requests at last to avoid cache bug in IE6-9. Issues#808
    for (var requestUri in requestCache) {
        if (requestCache.hasOwnProperty(requestUri)) {
            requestCache[requestUri]()
        }
    }
}

模块的状态是最关键的,模块状态的流转决定了加载的行为;
是否触发onload是由模块的_remian属性来确定,在load和子模块的onload函数中都对_remain进行了计算,如果为0,则表示模块加载完成,调用onload:

// Call this method when module is loaded
Module.prototype.onload = function () {
    var mod = this
    mod.status = STATUS.LOADED

    if (mod.callback) {
        mod.callback()
    }

    // Notify waiting modules to fire onload
    var waitings = mod._waitings
    var uri, m

    for (uri in waitings) {
        if (waitings.hasOwnProperty(uri)) {
            m = cachedMods[uri]
            m._remain -= waitings[uri]
            if (m._remain === 0) {
                m.onload()
            }
        }
    }

    // Reduce memory taken
    delete mod._waitings
    delete mod._remain
}

模块的_remain和_waitings是两个非常关键的属性,子模块通过_waitings获得父模块,通过_remain来判断模块是否加载完成。

继续阅读