行业内部诞生

网页的构造更为复杂,大约能够视作一个粗略应用程式,假若还像早先那样把具有的代码都停放贰个文书之中会有大器晚成对标题:

  • 全局变量相互影响
  • JavaScript文件变大,影响加载速度
  • 结构混乱、很难保险

和后端(比方Java)相比就能够看出显明的不一致。二〇〇八年RyanDahl创制了node.js项目,将JavaScript用于服务器编制程序,那证明“JS模块化编制程序”正式诞生。

基本原理

模块正是有个别功效的会晤,那么能够将二个大文件分割成一些小文件,在各种文件中定义区别的功力,然后在HTML中引进:

var module1 = new Object({
    _count : 0,
    m1 : function (){
        //...
    },
    m2 : function (){
        //...
    }
});

这么做的流弊是:把模块中保有的成员都展露了!我们明白函数的地面变量是万不得已从外部举行访谈的,那么能够用当下施行函数来优化:

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
        //...
    };
    var m2 = function(){
        //...
    };
    return {
        m1 : m1, m2 : m2
    };
})();

世家定义模块的秘籍或然丰富多彩,若是都能依据一定的科班来,那好处会要命大:能够相互援用!

模块标准

在node.js中定义math.js模块如下:

function add(a, b){
    return a + b;
}
exports.add = add;

在其余模块中央银行使的时候使用全局require函数加载就可以:

var math = require('math');
math.add(2,3);

在服务器上同步require是从未有过难题的,不过浏览器在互联网境遇就不可能这么玩了,于是有了异步的AMD规范:

require(['math'], function (math) {// require([module], callback);
    math.add(2, 3);
});

模块的概念方式如下(模块能够依附别的的模块):

define(function (){ // define([module], callback);
    var add = function (x,y){
        return x+y;
    };
    return { add: add };
});

用RequireJS可以加载超级多别的财富(看这里),很好很有力!在工作中用的非常多的是SeaJS,所运用的正儿八经称为CMD,发扬(应该是指异步格局):

as lazy as possible!

对此依附的模块的处理格局和英特尔的分别在于:

英特尔是提前实行(信任前置),CMD是延迟实践(正视就近)。

在CMD中定义模块的点子如下:

define(function(require, exports, module) {
    var a = require('./a');
    a.doSomething();
    var b = require('./b');
    b.doSomething();
});

应用格局向来看文档,这里就不赘述了!

SeaJS源码分析

刚接触模块化的时候感到这么些太轻巧了,不正是:

始建script标签的时候设置一下onload和src!

骨子里是那般的,但也不完全部都以!下边来开端看SeaJS的代码(sea-debug.js)。二个模块在加载的长河中或许资历上面两种景况:

var STATUS = 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,
    // 7 - 404
    ERROR: 7
}

内部存款和储蓄器中用Modul指标来维护模块的音讯:

function Module(uri, deps) {
    this.uri = uri
    this.dependencies = deps || [] // 依赖模块ID列表
    this.deps = {} // 依赖模块Module对象列表
    this.status = 0 // 状态
    this._entry = [] // 在模块加载完成之后需要调用callback的模块
}

在页面上运转模块系统必要使用seajs.use方法:

seajs.use(‘./main’, function(main) {// 依赖及回调方法
    main.init();
});

加载进度的完好逻辑能够在Module.prototype.load中看到:

Module.prototype.load = function() {
    var mod = this
    if (mod.status >= STATUS.LOADING) {
        return
    }
    mod.status = STATUS.LOADING
    var uris = mod.resolve() // 解析依赖模块的URL地址
    emit("load", uris)
    for (var i = 0, len = uris.length; i < len; i++) {
        mod.deps[mod.dependencies[i]] = Module.get(uris[i])// 从缓存取或创建
    }
    mod.pass(); // 将entry传递给依赖的但还没加载的模块
    if (mod._entry.length) {// 本模块加载完成
        mod.onload()
        return
    }
    var requestCache = {};
    var m;
    // 加载依赖的模块
    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()
        }
    }
    for (var requestUri in requestCache) {
        if (requestCache.hasOwnProperty(requestUri)) {
            requestCache[requestUri]()
        }
    }
}

完全上逻辑很顺就不讲了,独一相比较绕的正是_entry数组了。网络未有找到比较通俗易懂的篇章,于是瞅着代码连蒙带猜地大概看懂了,其实若是记住它的目的就能够:

当注重的具有模块加载成功后实践回调函数!

换种说法:

数组_entry中保留了当下模块加载成功今后、哪些模块的依附或许加载成功的列表(信任的反向关系)!

比方,模块A正视于模块B、C、D,那么通过pass之后的景况如下:

图片 1

此时A中的remain为3,相当于说它还应该有八个依赖的模块未有加载成功!而少年老成旦模块B注重模块E、F,那么在它load的时候会将A也传递出去:

图片 2

有几个细节:

  1. 生龙活虎度加载成功的模块不会被传出;
  2. 早就流传过一遍的模块不会重复传播;
  3. 借使依据的模块正在加载那么会递归传播;

保卫安全好凭仗关系随后就能够通过Module.prototype.fetch来加载模块,有三种sendRequest的落实际境况势:

  1. importScripts
  2. script

下一场依据结果实施load或者error格局。信任的兼具模块都加载成功后就能实践onload方法:

Module.prototype.onload = function() {
    var mod = this
    mod.status = STATUS.LOADED
    for (var i = 0, len = (mod._entry || []).length; i < len; i++) {
        var entry = mod._entry[i]
        if (--entry.remain === 0) {
            entry.callback()
        }
    }
    delete mod._entry
}

其中--entry.remain就一定于告诉entry对应的模块:你的依据列表里面早就有三个完成了!而entry.remain === 0则评释它所重视的保有的模块都早就加载成功了!那么那时候将施行回调函数:

for (var i = 0, len = uris.length; i < len; i++) {
    exports[i] = cachedMods[uris[i]].exec();
}
if (callback) {
    callback.apply(global, exports)// 执行回调函数
}

本子下载完结现在会登时试行define方式来尊崇模块的音讯:

从没显式地内定dependencies时会用parseDependencies来用正则相配方法中的require(卡塔尔(قطر‎片段(钦定重视列表是个好习贯)。

任何时候试行factory主意来扭转模块的数量:

var exports = isFunction(factory) ?
    factory.call(mod.exports = {}, require, mod.exports, mod) :
    factory

接下来实践你在seajs.use中定义的callback方法:

if (callback) {
    callback.apply(global, exports)
}

当你写的模块代码中require时,每趟都会奉行factory方法:

function require(id) {
    var m = mod.deps[id] || Module.get(require.resolve(id))
    if (m.status == STATUS.ERROR) {
        throw new Error('module was broken: ' + m.uri)
    }
    return m.exec()
}

到此地核心的逻辑基本上说罢了,补一张状态的改造图:

图片 3

事后在用的时候就足以说可瑞康(Karicare卡塔尔国些古怪的主题素材了!

总结

模块化相当好用,因而在ECMAScript
6中也起头帮助,不过浏览器扶助依然比较惦记的~~

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website