requirejs教程

随着前端项目的不断增大,js 文件越来越多,大部分都是 js 的依赖模块,传统的引入方式已经无法满足当前开发需求,急需模块化的方案来替我们管理这些依赖。
但,javascript 天生并不支持模块化,无法将多个模块文件分离出去,在使用时再将多个模块合并起来。事情总是需要解决,也总是不缺乏造轮子的人。
于是,前端模块化的始祖,require.js 出现了,其基于 AMD 规范。使用 define 函数定义模块,利用 require 函数 导入模块。详细使用方式请继续往下看。

获取方式

为什么要使用 require.js?

最早的时候,所有 Javascript 代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

1
2
3
4
5
6
  <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

这段代码依次加载多个 js 文件。

这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于 js 文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js 要在 2.js 的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

require.js 的诞生,就是为了解决这两个问题:

1
2
3
(1)实现js文件的异步加载,避免网页失去响应。

(2)管理模块之间的依赖性,便于代码的编写和维护。

模块加载规则

RequireJS 以一个相对于 baseUrl 的地址来加载所有的代码。 即 baseUrl + paths 的解析过程。

baseUrl的确定规则

  • 以含有 data-main 属性的 scripthtml 页面所在文件目录为 baseUrl

页面顶层 <script> 标签含有一个特殊的属性 data-mainrequire.js使用它来启动脚本加载过程,而baseUrl一般设置到与该属性相一致的目录。

  • paths 配置为准。(下面会介绍如何配置)

注:
RequireJS 默认假定所有的依赖资源都是 js 脚本,因此无需在 module ID 上再加 .js 后缀,RequireJS 在进行 module IDpath 的解析时会自动补上后缀。你可以通过 paths config 设置一组脚本,这些有助于我们在使用脚本时码更少的字。

有时候你想避开 baseUrl + paths 的解析过程,而是直接指定加载某一个目录下的脚本。此时可以这样做:如果一个 module ID 符合下述规则之一,其ID解析会避开常规的 baseUrl + paths 配置,而是直接将其加载为一个相对于当前 HTML 文档的脚本:

1
2
3
1. 以 ".js" 结束.
2. 以 "/" 开始.
3. 包含 URL 协议, 如 "http:" or "https:".

文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
|-www/
| |-libs
| | |- require.js
| | |- jquery.js
| | |- layer.js
| |-utils
| | |- dom.js
| |-pages
| | |-login
| | | |- login.html
| | | |- login.js
| |-style

login.html

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<title></title>
</head>
<body>

<div id="app"></div>

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

</body>
</html>

dom.js
该模块导出了一个对象。

1
2
3
4
5
define(function(){
return {
name: "dom module"
}
})

login.js

login.js 中,引用了 dom 模块,此时,baseUrllogin.html 所在的目录。所以,要想找到 dom.js,需要连跳两层文件夹,再进入 utils文件夹。

1
2
3
require(['../../utils/dom'] , function( dom ){
console.log( dom.name ) // dom module
})

这里有个需要注意的地方,如果你引用了那些定义了 模块名 的文件,是无法保证主逻辑文件和依赖文件的加载顺序的,也就是说,你很有可能获取不到依赖文件导出的变量。例如 jquery.js ,其内部 定义模块时,加上了模块名参数 jquery

1
2
3
define("jquery" , function(){
// ...
})

基于这种情况,require.js 会根据 paths 配置查找文件路径 (关于配置下面会介绍)。如果没有配置 paths 的话,require.js 只会简单的将该依赖文件通过含有 async 属性的 <script> 标签加载到 <head> 中,并不会处理文件之间的依赖关系。所以,当你引用一个含有模块名定义的依赖,又没有为其配置 paths 时,就会出现如下状况:

1
2
3
4
// login.js
require(['../../libs/jquery'] , function($){
console.log( $ ) // undefined
})

所以此时,我们需要对依赖进行配置 paths

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// login.js
require.config({
baseUrl: "/libs",
paths: {
jquery: "jquery"
}
})

require(['jquery'] , function($){
console.log( $ );
/*
ƒ ( selector, context ) {

// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery…

*/
})

现在,我们就可以拿到 jquery 导出的全局变量 $ 了。

API介绍

data-main属性

指定页面主逻辑文件的的路径,它有点像 c语言main()函数,所有的代码都从这开始运行。同时,它也默认确定了 baseUrl 的值为当前页面所在的目录。

require.config()配置方法

require.config.js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
require.config({
//配置模块的基本路径
//当在服务器模式下时,如果不使用 `/` 开头指定模块别名的路径的话,即当前的路径环境就是 根目录下的 assets 文件夹下
//通常,多页面开发中,我们将第三方资源放在 assets 文件夹中
baseUrl: '/assets',

//配置模块别名,也可以称作路径别名
//当配置 baseUrl 属性的 时候 , 此时就不需要加上 `/assets/` 了
// requirejs 会自动帮你拼上,同时,其默认处理的就是 js 文件,所以文件的 .js 后缀也不需要加。
paths : {
jquery : 'jquery/jquery.min'

//资源地址除了上面的 字符串格式,还可以是 数组格式,如:
//其作用是:优先加载远程资源,当加载不成功时,则加载本地资源文件
bootstrap : ['https://cdn.bootcss.com/bootstrap/4.0.0/js/bootstrap.min.js' , 'bootstrap/bootstrap.min' ],

foo : '../js/foo' // 配置一个自己定义的模块

//...

},

//处理不支持AMD模块化的第三方依赖
//比如 bootstrap ,其不支持 AMD 规范 ,即其没有使用 define 函数进行模块定义
//随后进行 define 函数 的 使用说明
shim : {

//需要 shim (垫) 的模块 , 上方已配置了 路径别名
bootstrap : {
//配置依赖项
deps : ['jquery'],
//配置导出变量名
exports : 'bootstrap'
}

//...
}

//其他配置项
// ...


})

// 至此,require.config.js 配置文件的配置就 基本完成了 ,当要引入其他模块时,继续配置即可。
// 支持AMD规范的 只需在 path 字段中 加个 配置 , 不支持 AMD 规范的 再在 shim 字段中 垫 一下即可。


define() 函数

AMD 规范要求 ,定义一个模块时 ,必须使用 define() 函数 来进行模块的 定义。

define 函数的参数说明

在定义一个模块时,其实 define 函数 接受 3个 参数。

参数1 : String类型,定义该模块的名字(一般不会写这个参数,使用路径的方式引用模块更易控)
参数2 : Array类型,该模块依赖,使用配置好的路径别名 或 路径
参数3 : callback, 该模块的主逻辑体 , callback 回调函数的 可接收 一些参数,这些参数就是 该模块的依赖项所导出的模块变量
值得注意的是,你需要按照 参数2 模块依赖 数组中参数的顺序来 配置 回调函数 的 形参顺序。

@returns : 任意类型,指定定义模块导出的接口。

例:

定义了一个 foo 模块,该模块依赖于 jquery
输出打印了一些信息
导出了一个对象

1
2
3
4
5
6
7
define(['jquery'] , function($){

console.log($)
console.log('这是foo模块')

return { name: "foo"} // 导出接口
})

require() 函数

有定义,就有引用,AMD规范中,使用 require() 函数 来引用模块。

require 函数的参数说明

参数1 : Array类型,模块依赖
参数2 : callback , 主体逻辑

例:

1
2
3
require(['jquery' , 'foo'] , function( $ ){
// your main logic code ...
});

最佳实践

在多页面应用中,每个页面都会对应一个主逻辑文件,那如果,我们每次都在 *.js 文件头部写配置信息的话,那就太蠢了。效率低下而不优雅。

所以,在项目根目录下,创建一个 require.config.js 文件,将配置信息写在这个文件中。当然,文件名你可以随便定义,最好能够区分她是谁的配置文件。

对于多页应用来说,通常我们会有如下项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

|-www/
| |-assets
| | |- requirejs
| | |- require.js
| | |- jquery
| | |- jquery.js
| | |- ...
| |-pages
| | |- login
| | |- login.html
| | |- login.js
| | |- demo
| | |- demo.html
| | |- demo.js
| |-utils
| | |- dom.js
| |-style
| |-require.config.js
| |-README.md

...

那么我们会这样使用模块化。

demo.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<title>demo</title>
</head>
<body>

<div id="app"> this is demo page </div>

<script src="/assets/requirejs/require.js"></script> <!-- 引入require.js -->
<script src="/require.config.js"></script> <!-- 引入requirejs的配置文件 -->
<script>
require(['../pages/demo/demo']) //页面的主入口文件
</script>

</body>
</html>

require.config.js

1
2
3
4
5
6
7
8
9
require.config({
baseUrl: "/assets",
paths: {
jquery: "jquery/jquery",
dom: "../utils/dom"
}

// ...
})

demo.js

1
2
3
4
5
require(['jquery' , 'dom'] , function($ , dom){
//该 主程序文件 依赖于 2个 模块 , 分别是 jquery, dom
//其中 dom 为 自己定义的 模块
// some code here...
})

也许你会想,哇!这不还是加载了 3 个 script 标签引用吗?

-- || 当你有 100 个 js 文件依赖时,你就不会这么想了!不信你可以试试。祝你好运!

兼容性

1
2
3
4
5
IE 6+ .......... 兼容 ✔
Firefox 2+ ..... 兼容 ✔
Safari 3.2+ .... 兼容 ✔
Chrome 3+ ...... 兼容 ✔
Opera 10+ ...... 兼容 ✔

结束语

这篇文章仅记录 require.js 的简单使用,可以帮助你快速入门,一般项目开发足够了,需要更多有关 require.js 的信息,可以翻阅 requirejs 官网