突然想记录点前端的东西。接触RequireJS是2012年,那时候觉得这个前端的模块化概念很酷,随即也在很多项目中使用了。现在JS的模块化已经随处可见了。基本上概念没什么变化,这个库也相当的容易上手,这里就用RequireJS写一个简单的例子。

先介绍下模块化概念,目前Javascript主要的模块化规范有两种,AMD和CommonJS。CommonJS是模块同步加载,即加载完后才会执行后续的代码,如同Java的import一样,所以比较适合后端的开发,比如NodeJS的模块系统就是根据CommonJS规范实现的。前端不一样,因为会有网络延迟,所以即便有一个(远程的)JS没有加载成功,你也要确保网页不会卡在那里,所以就需要异步模块加载功能。这就引入了AMD规范,全称是”Asynchronous Module Definition”。AMD一般的模块加载方式如下:

require(['module'], function(module) {
    // Code to be executed after module is loaded
});
// Code no need wait for module load

可以看到,它把依赖待加载模块的代码,放到了回调函数中,而其他代码不会被block。现在,让我们看看RequireJS是怎么做的。

  • 我们建立一个工程,目录结构如下

    requirejs
    |- js
    |    |- app
    |    |    |- app.js        // 自定义app模块
    |    |    |- math.js       // 自定义math模块
    |    |- lib
    |    |    |- require.js    // RequireJS库
    |    |- main.js            // 自定义的RequireJS入口
    |- index.html              // 首页
    

    其中,RequireJS库可从官网http://requirejs.org/docs/download.html下载。你可以选择”Minified”版本,也就是缩小化后的文件。

  • 编写首页,引入RequireJS库

<!doctype html>
<html>
    <head>
        <title>RequireJS Sample</title>
        <meta charset="utf-8">
    </head>
    <body>
        <div id="content"></div>
        <script src="js/lib/require.js" data-main="js/main"></script>
    </body>
</html>

我们在页面最后加载本地的require.js文件(为什么我就不说了吧),同时指定data-main属性指向js目录下的main.js,这里就指明了RequireJS的入口文件。注意入口文件后缀名.js可以省去,RequireJS默认会找JS文件。

  • 让我们先写两个模块,首先math.js
define(function () {
    var multiply = function(x, y) {
        return x * y;
    };

    return {
        multiply: multiply
    };
});

define方法就是用来定义模块,该方法接受一个匿名函数,并返回一个对象。这个对象就包含了该模块对外开放的接口。本例中,math.js开放了multiply接口,实现两数相乘的功能。

我们再写一个”app.js”

define(['math'], function(math) {
    var power = function(x) {
        return math.multiply(x, x);
    }

    return {
        power: power
    }
});

大家注意到,这个模块的define方法多了一个数组参数。这个参数指明了这个app模块依赖于其他哪些模块,并且通过后面匿名函数的参数,将那些依赖的模块传递进来。数组里可以定义多个依赖模块名,同时后面的匿名函数也可以有多个模块参数,并与数组中定义的模块名一一对应。本例中,app.js依赖math模块,并利用math中的multiply方法,实现求平方的power方法。app模块对外开放了power接口。

  • 现在让我们看下入口文件”main.js”该怎么写
require.config({
    baseUrl: 'js',    // 指定模块根目录,相对于index.html的路径,默认为main.js所在目录
    paths: {
        // 定义外部模块,jQuery从1.7后开始支持AMD规范
        jquery: 'http://lib.sinaapp.com/js/jquery/2.0.3/jquery-2.0.3.min',
        math: 'app/math',    // 找baseUrl下,app目录下的math.js,此处扩展名".js"可以省略
        app: 'app/app',
    }
});

// 如不配置require.config(),则默认找main.js所在目录下的jquery.js和app.js
require(['jquery', 'app'], function($, app) {
    $('#content').text(app.power(5));
    console.log($().jquery);
});

代码中,require.config()定义了这个工程所需要的模块,并指明模块文件的具体位置。而require()方法就指明这个工程所依赖的模块及加载模块后所需执行的操作。require()方法的参数同define()方法基本一致:第一个数组参数指明所依赖的模块名称,第二个参数匿名函数传入具体的模块对象,匿名函数的参数同数组里的模块名一一对应。本例中,require()方法会在页面上显示”5”的平方值,并在控制台打印出当前加载的jquery库的版本。

  • 我们来看下运行结果 将工程放到你的web server根目录下,然后打开地址http://localhost/requirejs,有没有看到你想要的结果呢?

本例中的代码可以在这里下载