AMD模块加载

  30 Apr 2016


概述

node作为一个服务端语言,在加载模块时,模块都是在本地磁盘,因此可以以可观的速度加载进来,因此,在node里的模块加载都是同步的,阻塞时间都不会太长。 如果我们要在浏览器端的js来实现模块设计模式呢,这就意味着我们需要远程从服务器加载所以定义的模块,浏览器js引擎的阻塞时间可想而知。 如果不使用模块设计模式的话,那各个js文件互相依赖,而且共享一个顶层作用域window,很容易出现变量重定义的错误。比如下面这种形式。

  <script src="js/test1.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/test2.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/test3.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/test4.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/test5.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/test6.js" type="text/javascript" charset="utf-8"></script>

各个文件互相依赖,耦合性很高。

但是如果我们要用模块设计模式的话,那么各个模块必须用异步加载,如果不用异步加载,浏览器很可能处于很长时间的阻塞状态,造成很糟糕的用户体验。因此AMD孕育而生。


AMD 模块定义

define(id,arr[],function(){});

define 接受3个参数,id是模块化标识可以省略,第二个参数可以省略,在没有依赖于其他模块的情况下,可以将第一个参数省略,因此,我们可以这样定义。

define(() => {
    let sum = (x,y) => {
        return x + y;
    };
    return {sum:sum}
});

与commonJS 也有一些相似之处,commonJS 暴露给外部的接口为module.exports 所指向的对象,而AMD 暴露给外部的接口为return 的对象, 这里我暴露了一个json对象,key 为外部调用的名称,value 为名称指向的对象。

如果我们定义的模块依赖于其他的模块,比如我们依赖于一个hello的模块。

这时,我们的模块需要这样定义:

define(['hello'],() => {
    let sum = (x,y) => {
        return x + y;
    };
    return {sum:sum}
});

定义数据对象模块 (data.js)

define({
    users: [],
    members: []
});

RequireJS

RequireJS

现在有很多AMD规范的库 RequireJS就很不错········呵呵呵别的懒得看了而已

看个极简的例子:

首先要到requirejs的网站去下载require.js

<!DOCTYPE html>
//index.html
<html>
    <head>
        <script type="text/javascript" src="require.js"></script>
        <script type="text/javascript">
            require(["a"]);
        </script>
    </head>
    <body>
      <span>body</span>
    </body>
</html>

//a.js
define(function(){
    function fun1(){
      alert("it works");
    }

    fun1();
})

requireJS 定义了三个变量:define,require,requirejs,其中requirejs===require,一般用require 因为懒是天性。

  • define 定义一个模块。
  • require 加载依赖模块,并执行加载完成后的回调函数。

通过define函数定义了一个模块,然后再页面中使用:

require(["js/a"]);

来加载该模块(注意require中的依赖是一个数组,即使只有一个依赖,你也必须使用数组来定义),require API的第二个参数是callback,一个function,是用来处理加载完毕后的逻辑,如:

require(["js/a"],function(){
    alert("load finished");
})

RequireJS全局配置

我们可以通过require.config来配置复杂的加载文件

require.config({
    paths : {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery","js/jquery"],
        "a" : "js/a"
    }
})

paths还有一个重要的功能,就是可以配置多个路径,如果远程cdn库没有加载成功,可以加载本地的库.

我们不可能每个文件都写require.config,requirejs提供了一种叫”主数据”的功能,我们首先创建一个main.js

//main.js
require.config({
    paths : {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"],
        "a" : "js/a"
    }
})
//通过data-main这种方式来使用
<script data-main="js/main" src="js/require.js"></script>

加载requirejs脚本的script标签加入了data-main属性,这个属性指定的js将在加载完reuqire.js后处理,我们把require.config的配置加入到data-main后,就可以使每一个页面都使用这个配置,然后页面中就可以直接使用require来加载所有的短模块名

data-main还有一个重要的功能,当script标签指定data-main属性时,require会默认的将data-main指定的js为根路径


RequireJS shim

为那些没有使用define()来声明依赖关系、设置模块的”浏览器全局变量注入”型脚本做依赖和导出配置。

require.config({
    shim: {
        "backbone" : {
        //在加载backbone.js之前应先加载它的依赖函数underscore.js和jquery.js
         deps: ['underscore', 'jquery'],
         //加载完毕后该模块使用的引用名
         exports: 'Backbone'
        }
    }
})