前端模块的历史进程

模块机制产生原因

最初我们是这么写代码的

function foo(){
    //...
}
function bar(){
    //...
}

Global 被污染,很容易命名冲突

简单封装:Namespace 模式

var MYAPP = {
    foo: function(){},
    bar: function(){}
}

MYAPP.foo();

这样减少 Global 上的变量数目,但本质是对象

IIFE 模式

var Module = (function(){
    var _private = "safe now";
    var foo = function(){
        console.log(_private)
    }

    return {
        foo: foo
    }
})()

Module.foo();
Module._private; // undefined

再接下来,我们可以在模块中引入一些其他依赖,比如传入一个jquery的引用

var Module = (function($){
    var _$body = $("body");     // we can use jQuery now!
    var foo = function(){
        console.log(_$body);    // 特权方法
    }

    // Revelation Pattern
    return {
        foo: foo
    }
})(jQuery)

Module.foo();

这就是模块模式,也是现代模块实现的基石。

script加载

script(src="jquery.js")
script(src="app.js") 

一开始我们用script标签加载js是这样的,这样的写的缺点是顺序很重要,最基础的依赖只能放最前面,因为浏览器按照< script>在网页中出现的顺序,读取Javascript文件,然后立即运行。
同时,浏览器采用”同步模式”加载< script>标签,也就是说,页面会”堵塞”(blocking),等待javascript文件加载完成。如果我们通过异步的方式加载js,又会导致在onload的回调中,不能调到js中的方法。
这就带来了几个问题:

  • 难以维护
  • 依赖模糊
  • 请求过多

LABjs

LABjs帮助我们更有效地管理Javascript加载。 LAB(Loading and Blocking),Loading 指异步并行加载,Blocking 是指同步等待执行。LAB通过优雅的语法(script 和 wait)实现了这两大特性,核心价值是性能优化。LABjs 是一个文件加载器。

YUI

YUI实现了基于模块的依赖管理

// YUI - 编写模块
YUI.add('dom', function(Y) {
  Y.DOM = { ... }
})

// YUI - 使用模块
YUI().use('dom', function(Y) {
  Y.DOM.doSomeThing();
  // use some methods DOM attach to Y
})

YUI的原理如下

// Sandbox Implementation
function Sandbox() {
    // initialize the required modules
    for (i = 0; i < modules.length; i += 1) {
        Sandbox.modules[modules[i]](this);
    }
}

所有依赖模块通过 attach 的方式被注入沙盒(attach:在当前 YUI 实例上执行模块的初始化代码,使得模块在当前实例上可用)。也就是说,在使用add 方法之后,YUI对模块进行了初始化。

Combo

script(src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js")
script(src="http://yui.yahooapis.com/3.0.0/build/dom/dom-min.js")

我们要解决的问题之一是script标签的请求太多,那么可以通过这样的方式:

script(src="http://yui.yahooapis.com/combo?
    3.0.0/build/yui/yui-min.js&
    3.0.0/build/dom/dom-min.js")

这样的方式需要web服务器的支持。

commonJs

commonJs标准解决的不止是浏览器,在node.js中得到了支持

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

使用的时候

// main.js
var math = require('math')      // ./math in node
console.log(math.add(1, 2));    // 3

值得一提的是,同步加载对服务器/本地环境根本不是问题。

AMD和CMD

CMD(Common Module Definition)是SeaJS 对模块定义的规范化产出,而AMD(Async Module Definition)是RequireJS 对模块定义的规范化产出。
如果require是异步的,那么以下代码就会报错:

//CommonJS Syntax
var Employee = require("types/Employee");

function Programmer (){
    //do something
}

Programmer.prototype = new Employee();

//如果 require call 是异步的,那么肯定 error
//因为在执行这句前 Employee 模块根本来不及加载进来

所以在AMD中就通过wrapper

//AMD Wrapper
define(
    ["types/Employee"],  //依赖
    function(Employee){  //这个回调会在所有依赖都被加载后才执行
        function Programmer(){
            //do something
        };

        Programmer.prototype = new Employee();
        return Programmer;  //return Constructor
    }
)

require.js

// 在helper.js中
define('helper', ['jquery'], function($){
    return {
        trim: function(str) {
            return $.trim(str);
        }
    }
})

第一个参数为模块名,如果不写的话,就是以文件路径为模块名(相应的,在require调用中的依赖就为路径字符串)。第二个参数为模块依赖,是一个数组,可以配置多个依赖。最后在模块依赖加载之后,会触发第三个参数的回调,参数的顺序为依赖的模块顺序。在回调中返回一个模块结果。接下来看一下加载模块

// 在所需js文件中
require(['helper'], function(helper){
    var str = helper.trim(' sysuzhyupeng ');
    console.log(str);
});

加载模块中第一个参数同样是依赖的数组,使用方式和定义模块基本一致。
require.js以一个相对于baseUrl的地址来加载所有代码,比如上面我们require的时候,会在helper前面加上baseUrl路径。baseUrl可以在require.js加载的script标签以data-main属性配置,也可以自己配置

requirejs.config({
    baseUrl: '',
    paths: {
        'jquery': 'lib/jquery'
    }
})

那么对于非baseUrl下的模块,可以通过配置paths字段来指定路径。
require.js以< script>标签来加载文件,可以跨域获取cdn上的文件。当我们调用require的时候,它加载后马上执行。
不支持AMD的模块只需要在配置文件中的shim属性中配置即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值