JustYeh的前端博客

RequireJS参考手册

2019-07-02

模块化js

关于RequireJS

RequireJS 是一个JavaScript模块加载器。它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJS加载模块化脚本将提高代码的加载速度和质量。

兼容性

  • IE 6+ .......... 兼容 ✔
  • Firefox 2+ ..... 兼容 ✔
  • Safari 3.2+ .... 兼容 ✔
  • Chrome 3+ ...... 兼容 ✔
  • Opera 10+ ...... 兼容 ✔

中文官网:http://www.requirejs.cn/

github:https://github.com/requirejs/requirejs

关于JavaScript模块化,本站有一篇专门的文章介绍它:JavaScript模块化开发

为什么要用RequireJS

随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,就像是这样:

<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
<script src="d.js"></script>
...
<script src="z.js"></script>

原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作、模块复用、单元测试等等一系列复杂的需求。

RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一。最新版本的RequireJS压缩后只有14K,堪称非常轻量。

  • 实现了js文件的按需、异步加载
  • 管理模块之间的依赖性,便于代码的编写和维护;
  • 利于代码的模块化,便于团队、协作开发;

使用RequireJS

通常在body的最后引入js文件:

<script src="//cdn.bootcss.com/require.js/2.3.2/require.min.js" data-main="js/main.js"></script>

其中data-main属性用来指定程序的入口模块,RequireJS使用它来启动脚本加载过程。

RequireJS以一个相对于baseUrl的地址来加载所有的代码,而baseUrl一般设置到与data-main性相一致的目录。

RequireJS默��假定所有的依赖资源都是js脚本,因此无需在module ID上再加".js"后缀,RequireJS在进行module ID到path的解析时会自动补上后缀。

主模块main.js

下面是一个主模块文件的实例:

目录结构:

|js/
|--main.js
|----ripplejs/
|------ripple.js

main.js:

require.config({
paths:{
'jquery':'http://cdn.bootcss.com/jquery/1.9.1/jquery.min',
'ripple':'ripplejs/ripple',
},
shim:{
'ripple':['jquery']
}
});
require(['jquery','ripple'], function ($,ripple){
  $(function(){
var withRipples = [".withripple",].join(",");
$(withRipples).ripples();
});
});

require()函数

require()函数接受两个参数:

  • 第一个参数是一个数组,表示所依赖的模块,上例就是['jquery', 'ripple'],即主模块依赖这两个模块。
  • 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。

使用define()定义模块

RequireJS加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

简单的值对

如果一个模块仅含值对,没有任何依赖,则在define()中定义这些值对就好了:

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

模块定义为一个函数

假定现在有一个stu模块,它定义了一个getName()方法,那么stu.js就要这样写:

define(function (){
return{
getName:function (){
return 'justyeh'
}
}
};

我们可以这样使用它:

require.config({
paths:{
stu:"stu"
}
})
require(['stu'],function(stu){
alert(stu.getName())
})

带有依赖的模块

如果这个模块还依赖其他模块,那么define()函数需要设置一个数组参数,指明该模块的依赖性。

define(['jquery'],function ($){
return{
getName:function (){
return $("#name").val();
}
}
};

命名模块

你可能会看到一些define()中包含了一个模块名称作为首个参数:

define('jquery', [], function() { ... });

运行下面这段代码:

requirejs.config({
paths: {
    myjquery: '//cdn.bootcss.com/jquery/3.1.1/jquery.min'
  }
});
requirejs(['myjquery'], function($) {
console.log($);
});

它会提示你:undefined,现在改个名字:

requirejs.config({
    paths: {
        jquery: '//cdn.bootcss.com/jquery/3.1.1/jquery.min'
    }
});
requirejs(['jquery'], function($) {
    alert($);
});

这时输出正确了,为什么jquery模块必须命名为jquery,因为jquery是一个命名模块;命名模块表示给当前这个模块起了名字,它已经是有主的了,只能属于jquery。所以当我们使用另一个名字myjquery去引用这个库的时候,它会发现,在 jquery.js 里声明的模块名jquery与我自己使用的模块名myjquery不同,便不会把它赋给myjquery,所以myjquery的值是undefined。

所以我们在使用一个第三方的时候,一定要注意它是否声明了一个确定的模块名。

配置选配

baseUrl

所有模块的查找根路径。如未显式设置baseUrl,则默认值是加载require.js的HTML所处的位置。如果用了data-main属性,则该路径就变成baseUrl。

paths

path映射那些不直接放置于baseUrl下的模块名。设置path时起始位置是相对于baseUrl的,除非该path设置以"/"开头或含有URL协议(如http:)。

用于模块名的path不应含有.js后缀,因为一个path有可能映射到一个目录。路径解析机制会自动在映射模块名到path时添加上.js后缀。

在浏览器中运行时,可指定路径的备选(fallbacks),比如:

requirejs.config({  
    paths: {
        jquery: ['//cdn...1','//cdn...3','//cdn...2']
  }
});

以实现诸如首先指定了从CDN中加载,一旦CDN加载失败则从本地位置中加载这类的机制。

shim

理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。

在这种情况下,我们要使用 shim ,将某个依赖中的某个全局变量暴露给requirejs,当作这个模块本身的引用。

在下面的示例中,underscore和backbone这两个库,都没有采用AMD规范编写,我们可以这样使用:

requirejs.config({
    //The shimconfig will not work correctly if used on AMD scripts,
    //in particular, the exports and init config will notbe triggered, and the deps config will be confusingfor those cases.
    shim: {
        'backbone': {
            //在加载backbone.js之前应先加载它的依赖函数underscore.js和jquery.js
            deps: ['underscore', 'jquery'],
            //加载完毕后该模块使用的引用名
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        }
});

//Then, later in a separate file, call it 'MyModel.js', a module is defined,specifting 'backbone' as a dependency.
//RequireJS will use the shim config to properly load 'backbone' and give a local reference to this module.
//The global Backbone will still exist on the page too.
define(['backbone'], function (Backbone) {
  return Backbone.Model.extend({});
});

那些仅作为jQuery或Backbone的插件存在而不导出任何模块变量的"模块"们,shim配置可简单设置为依赖数组:

requirejs.config({
    shim: {
        'jquery.colorize': ['jquery'],
        'jquery.scroll': ['jquery'],
        'backbone.layoutmanager': ['backbone']
    }
});

shim配置仅设置了代码的依赖关系,想要实际加载shim指定的或涉及的模块,仍然需要一个常规的require/define调用。设置shim本身不会触发代码的加载。

map

对于给定的模块前缀,使用一个不同的模块ID来加载该模块。

该手段对于某些大型项目很重要:如有两类模块需要使用不同版本的"foo",但它们之间仍需要一定的协同。 在那些基于上下文的多版本实现中很难做到这一点。而且,paths配置仅用于为模块ID设置root paths,而不是为了将一个模块ID映射到另一个。map示例:

requirejs.config({
    map: {
        'some/newmodule': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

如果各模块在磁盘上分布如下:

|foo1.0.js |foo1.2.js |some/ |--newmodule.js |--oldmodule.js

当“some/newmodule”调用了“require('foo')”,它将获取到foo1.2.js文件;而当“some/oldmodule”调用“`require('foo')”时它将获取到foo1.0.js。

该特性仅适用于那些调用了define()并将其注册为匿名模块的真正AMD模块脚本。并且,请在map配置中仅使用绝对模块ID,“../some/thing”之类的相对ID不能工作。

另外在map中支持“”,意思是“对于所有的模块加载,使用本map配置”。如果还有更细化的map配置,会优先于“”配置。示例:

requirejs.config({
    map: {
        '*': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

意思是除了“some/oldmodule”外的所有模块,当要用“foo”时,使用“foo1.2”来替代。对于“some/oldmodule”自己,则使用“foo1.0”。

config

常常需要将配置信息传给一个模块。这些配置往往是application级别的信息,需要一个手段将它们向下传递给模块。在RequireJS中,基于requirejs.config()的config配置项来实现。要获取这些信息的模块可以加载特殊的依赖“module”,并调用module.config()。示例:

requirejs.config({
    config: {
        'bar': {
            size: 'large'
        },
        'baz': {
            color: 'blue'
        }
    }
});

//bar.js, which uses simplified CJS wrapping:
//http://requirejs.org/docs/whyamd.html#sugar
define(function (require, exports, module) {
    //Will be the value 'large'
    var size = module.config().size;
});

//baz.js which uses a dependency array,
//it asks for the special module ID, 'module':
//https://github.com/jrburke/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#wiki-magic
define(['module'], function (module) {
    //Will be the value 'blue'
    var color = module.config().color;
});

若要将config传给包,将目标设置为包的主模块而不是包ID:

requirejs.config({
    //Pass an API key for use in the pixie package'smain module.
    config: {
        'pixie/index': {
            apiKey: 'XJKDLNS'
        }
    },
    //Set up config for the "pixie" package, whose mainmodule is the index.js file in the pixie folder.
    packages: [
        {
            name: 'pixie',
            main: 'index'
        }
    ]
});

packages

从CommonJS包(package)中加载模块。

nodeIdCompat

在放弃加载一个脚本之前等待的秒数。设为0禁用等待超时。默认为7秒。

waitSeconds

命名一个加载上下文。这允许require.js在同一页面上加载模块的多个版本,如果每个顶层require调用都指定了一个唯一的上下文字符串。

deps

指定要加载的一个依赖数组。当将require设置为一个config object在加载require.js之前使用时很有用。一旦require.js被定义,这些依赖就已加载。使用deps就像调用require([]),但它在loader处理配置完毕之后就立即生效。它并不阻塞其他的require()调用,它仅是指定某些模块作为config块的一部分而异步加载的手段而已。

callback

在deps加载完毕后执行的函数。当将require设置为一个config object在加载require.js之前使用时很有用,其作为配置的deps数组加载完毕后为require指定的函数。

enforceDefine

如果设置为true,则当一个脚本不是通过define()定义且不具备可供检查的shim导出字串值时,就会抛出错误。

xhtml

如果设置为true,则使用document.createElementNS()去创建script元素。

urlArgs

RequireJS获取资源时附加在URL后面的额外的query参数。作为浏览器或服务器未正确配置时的“cache bust”手段很有用。使用cache bust配置的一个示例:

urlArgs: "bust=" +  (new Date()).getTime()

在开发中这很有用,但请记得在部署到生成环境之前移除它。

scriptType

指定RequireJS将script标签插入document时所用的type=""值。默认为“text/javascript”。想要启用Firefox的JavaScript 1.8特性,可使用值“text/javascript;version=1.8”。