RequireJS 详解

简介

一个复杂的应用,其编程语言也必须要有模块机制,方能更好的组织代码。在前端 js 模块中,RequireJS 是一个非常小巧的 JavaScript 模块载入框架,是 AMD 规范最好的实现者之一。

特点

异步加载模块

当我们引入 js 文件时,不会阻塞浏览器对 html 文档的渲染。

按依赖顺序加载

我们不用在当心 js 文件的引入顺序,RequireJS 会自动帮我们处理依赖关系。

模块依赖前置

建议所有的依赖在模块开始就全部声明加载,不过现在也提供就近加载的方法。

使用

其实 RequireJS 的使用非常简单,核心只有两个函数:

  • define
  • require

引入 RequireJS

下载方式:

  • 官网
  • BootCDN

js/lib/index.js

(function () {
    alert('Hello index')
})()

index.html

<script src="scripts/require.js"></script>
<script>
    require(["js/lib/index.js"], function () {
        // do something after loading index.js
    })
<script>

RequireJS 配置函数

如果有些路径过长,有时候我们希望能使用别名代替,这个时候可以使用 requirejs.config 函数。

<script src="scripts/require.js"></script>
<script>
    requirejs.config({
        //By default load any module IDs from js/lib
        baseUrl: 'js/lib',
        //except, if the module ID starts with "app",
        //load it from the js/app directory. paths
        //config is relative to the baseUrl, and
        //never includes a ".js" extension since
        //the paths config could be for a directory.
        paths: {
            "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"]
        }
    })
    require(["index.js", "jquery"], function (index, $) {
        // do something after loading index.js
    })
<script>

如果我们页面过多,总是写 requirejs.config 不太好,感觉 ugly。RequireJS 为我们提供了一种方式。

<script data-main="js/app.js" src="js/require.js"></script>

在 script 标签中,加入 data-main 属性。这样会默认加载 app.js,而我们只需要在 app.js 中填写配置信息即可。

app.js

requirejs.config({
    //By default load any module IDs from js/lib
    baseUrl: 'js/lib',
    //except, if the module ID starts with "app",
    //load it from the js/app directory. paths
    //config is relative to the baseUrl, and
    //never includes a ".js" extension since
    //the paths config could be for a directory.
    paths: {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"]
    }
})
require(["index.js", "jquery"], function (index, $) {
    // do something after loading index.js
})

第三方模块

通过 require 加载的模块一般都需要符合 AMD 规范,即使用 define 来申明模块。但是部分时候需要加载非 AMD 规范的 js,这时候就需要用到另一个功能:shim。

比如我要是用 underscore 类库,但是他并没有实现 AMD 规范,那我们可以这样配置:

// 配置
require.config({
    shim: {
        "underscore" : {
            exports : "_"
        }
    }
})

// 使用
require(["underscore"], function(_){
    _.each([1,2,3], alert)
})

本质上来说,就是做了一次 RequireJS 的模块定义。

define

这个函数用来定义模块。我试着思考一下,如果设计一个模块新系统。无非就是三块:

  1. 定义模块
  2. 引用模块
  3. 查找模块

这里我们不对其中的原理进行深入探究,先从简单的使用说起。define 顾名思义,就是用来定义一个模块的。

函数介绍

define(id?, dependencies?, factory)

其中 ? 表示该参数可选

定义简单对象模块

define({
    color: "black",
    size: "unisize"
})

这样定义的对象如果被多处引用,那么引用的地方会共享这个对象的引用。

定义副本对象模块

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
})

通过函数返回一个对象的副本,引用的地方对对象的修改不会相互影响,这是一种典型的工厂模式

定义需要依赖其他模块的对象模块

define(["./cart", "./inventory"], function(cart, inventory) {
    //return an object to define the "my/shirt" module.
    return {
        color: "blue",
        size: "large",
        addToCart: function() {
            inventory.decrement(this)
            cart.add(this)
        }
    }
})

这里依赖了 cartinventory 模块,然后通过回调函数的参数,可以获取两个模块的引用。

定义一个函数模块

define(["my/cart", "my/inventory"], function(cart, inventory) {
    //return a function to define "foo/title".
    //It gets or sets the window title.
    return function(title) {
        return title ? (window.title = title) : inventory.storeName + ' ' + cart.name
    }
})

这个模块最后返回的值是一个函数,而非一个对象。

定义一个有名字的模块

define("foo/title", ["my/cart", "my/inventory"], function(cart, inventory) {
    //Define foo/title object in here.
})

理论上来讲,我们定义一个模块是需要名字的。但是实际中,一个模块就是一个文件,而文件名就是模块的名字,并且我们会在 require.config() 中定义别名。所以一般我们是不需要给模块加名字的。

另一种依赖引入方式

define(["require", "./relative/name"], function(require) {
    var mod = require("./relative/name");
});
define(function(require) {
    var mod = require("./relative/name");
});

这种写法应该说和 RequireJS 的初衷相违背,因为 RequireJS 提倡依赖前置,意思是说模块中会用到的依赖在模块最初就加载好,而不是在会用到的地方在加载。

举例来说:你有个模块有 800 行代码,但所依赖的其他模块在 500 行才会有用到。可能你希望,在 500 行的地方再加载所依赖的模块。这种情况叫就近依赖,CMD 规范就是如此。不过现在 RequireJS 也支持就近依赖了。

require

函数介绍

require(dependencies?, factory)

用法

require(["http://example.com/api/data.json?callback=define"],
    function (data) {
        //The data object will be the API response for the
        //JSONP data call.
        console.log(data)
    }
)

观察 require 和 define 函数,我们可以发现,它们有一个参数的差异。require 没有 id 参数。

老实说这个 require 和 define 函数内部机制差不多,不一样的地方是 define 的回调函数需要有 return 语句返回模块对象,这样 define 定义的模块才能被其他模块引用;require 的回调函数不需要 return 语句。

实际中,我们只会在顶层模块(不需要被其他模块调用)才会使用 require 函数。


参考

  • RequireJS 官网
  • JS模块化工具requirejs教程(二):基本知识

你可能感兴趣的:(Web前端)