文章目录
  1. 1. 简介
  2. 2. 解决了什么问题?
  3. 3. 如何使用?
    1. 3.1. 主页面
    2. 3.2. 主模块
    3. 3.3. 其他模块
    4. 3.4. 运行结果
  4. 4. AMD模块的写法
    1. 4.1. 不依赖其他模块
    2. 4.2. 依赖其他模块
    3. 4.3. jQuery
    4. 4.4. 加载非规范的模块
  5. 5. requireJS 插件
  6. 6. 其他
  7. 7. 参考

本系列博客更像是读blog读文档的笔记,从诸多资料中梳理出了自己的认识。

JS 的模块化编程(一)- 模块的基本写法
JS 的模块化编程(二)- CommonJS
JS 的模块化编程(三)- AMD
JS 的模块化编程(四)- CMD
JS 的模块化编程(五)- AMD 和 CMD 的区别
JS 的模块化编程(六)- requireJS

简介

requireJS 就是一个工具库,用户客户端的模块化管理。
它的模块管理遵守 AMD 规范。它让客户端的代码可以分成一个模块,实现异步或动态加载,提高代码的性能和维护性。

如何使用 requireJS:

  • 通过 define() 将代码定义成模块
  • 通过 require() 实现代码的模块加载
  • 要求每个模块单独放在一个单独的文件里

解决了什么问题?

早期,所有的 JS 代码都写在一个文件里,只要加载这一个文件就可以了。后来,代码越来越多,就分成了多个文件,再依次加载。诸如:

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 文件。但有缺点:
1.加载时会阻塞浏览器渲染网页。文件越多,网页失去响应的时间就越长。
2.当 JS 文件间存在依赖关系时,需要人工严格保证加载的顺序。当依赖关系复杂的时候,就会难以维护。

为了解决以上这两问题,requireJS 诞生了。
1.实现 JS 的异步加载,避免网页失去响应。
2.管理模块间的依赖性,便于代码的编写和维护。

如何使用?

主页面

首先,去官网下载 requireJS 的最新版本。下载后,在主页面 index.html 中引用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>requireJS 入门小练习</title>
</head>
<body>
<!-- data-main 属性指主模块|入口模块,主模块是整个网页的入口代码,类似C语言的main()函数 -->
<script src="require.js" data-main="js/main.js"></script>
</body>
</html>

只需用 script 标签引入 require.js 即可,其他的文件模块都不再使用 script 标签引入

主模块

主模块 main.js
常见的情况是主模块依赖其他模块,这时就使用 AMD 规范定义的 require() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require.config({
baseUrl: 'js/',
paths: {
jquery: 'jquery-1.11.2' // 【默认省略.js,否则写着.js会报错】
}
});
require(['jquery', 'math'], function($, math){
alert($().jquery); //弹框显示当前jQuery的版本
var ele = math.sum(4,5);
console.log(ele);
});

main.js 中有两个函数调用

  1. require.config()
    配置一些参数,它将会影响到 requirejs 库的一些行为。常用的配置有baseUrl,path:
    (1)baseUrl 来配置模块根目录:若没写-则path的路径默认是和 main.js 同层;一旦写了则是相对于 index.html;当然也可是绝对路径
    (2)path 属性指定各个模块的加载路径:其中jquery是模块的名字,值是路径(它的根路径可由baseUrl来定义)
  2. require() 有两个参数
    (1)第一个参数是一个数组,表示所依赖的模块名,是字符串类型
    (2)第二个参数是回调函数,加载的模块会以参数形式传入该函数,从而实现在回调函数内部使用这些模块

require() 异步加载各个模块,浏览器不会失去响应。它指定的回调函数,只有当前面的模块都加载成功后,才会运行。解决了依赖性问题。

说明:

  1. 如果 require.config() 里的 baseUrl
    (1)baseUrl 没有定义,则默认是和主模块 main.js 同目录
    (2)baseUrl 的值是空字符串’’,则默认是和主页面 index.html 同目录
  2. 如果 require() 里的模块名在 require.config() 的 paths 里没有定义,比如上例中的 ‘math’,则默认路径是和主模块 main.js 同目录,且默认文件名字是 模块名.js。
  3. 在 require 中是以模块名字为唯一标识的。【符合预期嘛,减少相同模块的重复加载。但使用不当会造成文件的覆盖】
    (1)主模板依赖了 jquery,如果子模块也依赖 jquery,且模块的名字和主模块里的相同,都是’jquery’,那么整个程序就只加载其中一个,且只加载一次。具体加载谁,依据依赖顺序(因为在子模块里也可以重新设置 require.config() 去配置)。此例会优先加载主模块里的 jquery。
    (2)如果不同的子模块都用到了 cookie 插件,但 cookie 里的代码不同。在使用时,请将两个模块名字赋成不同的值,否则会造成 js 的相互覆盖。

其他模块

math.js 的内容:

1
2
3
4
5
6
7
8
9
10
define(function($){
function sum(a, b){
return a+b;
}
return {
sum: sum
};
});

由于 requireJS 加载的模块是采用的 AMD 规范,所以要用 requireJS 来加载的模块也必须按照 AMD 的规范来写。必须采用特定的 define() 函数来定义。详情参考AMD模块的写法

运行结果

文件的目录结构:

当我们在浏览器中打开 index.html 页面时,可以看到除了 require.js 外,main.js、jquery.js 和 math.js 也都请求了。后三个正是通过 require 请求的。

这是一个很简单的例子,使用 requireJS 动态加载 jquery.js 和 math.js,知识点:

  1. data-main 属性:指出主模块
  2. require.config() 自定义模块的加载行为
  3. require() 异步加载各个模块
  4. define() 定义一个函数类型模块。requireJS 的模块可以是JS对象、函数、或其他类型(CommonJS/SeaJS则只能是JS对象)

说明:
requireJS 要求每个模块都是一个单独的 js 文件,这样,如果加载多个模块,就会发出多次 HTTP 请求,会影响网页的加载速度。所以,requireJS 提供了优化工具,当模块部署完毕后,可以用这个工具将多个模块合并在一个文件中。

AMD模块的写法

requireJS 加载的模块采用 AMD 规范,也就是模块必须按照 AMD 的规范来写,模块必须采用特定的 define() 函数来定义。

不依赖其他模块

如果一个模块不依赖其他模块,那么就可以直接定义在 define() 函数中了。比如 math.js

1
2
3
4
5
6
7
8
9
10
define(function(){
function sum(a, b){
return a+b;
}
return {
sum: sum
};
});

依赖其他模块

如果这个模块依赖于其他模块,比如:animate.js
【Q.那此处的模块名,也是直接在main里定义的那些吗?还是在各自的js里再次 require.config() 会覆盖么?try下~】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
define(['jquery'], function($){
function setPosition(selector, left, top){
$(selector).css({
'position': 'absolute',
'left': left,
'top': top
});
}
return {
setPosition: setPosition
};
});

jQuery

jQuery 从1.7后开始支持 AMD 规范,即如果jQuery作为一个 AMD 模块运行时,它的模块名是”jquery”,注意”jquery”是固定的。

jQuery 中支持 AMD 的代码如下:

1
2
3
4
5
if( typeof define === 'function' && define.amd && define.amd.jQuery ){
define( "jquery", [], function(){
return jQuery;
} );
}

注:如果本程序里的 js 名字就是 jquery.js 而不是 jquery-1.11.2.js,那么 require.config 中的 paths 中的 jquery 就可以省略了。

jQuery 最终向外暴露的是全局的 jQuery 和 $,即 window.jQuery = window.$ = jQuery
如果将 jQuery 应用在模块化开发时,其实可以不用全局的,即可以不暴露出来。需要用到 jQuery 时使用 require() 函数即可。

加载非规范的模块

理论上,requireJS 加载的模块必须是按照 AMD规范、用 define() 定义的模块。但是实际上,requireJS 也能够加载非规范的模块。这样的模块们,在用 require() 加载之前,要先用 require.config() 定义它们的一些特性。 shim 属性专门用来配置不兼容的模块。举例,underscore 和 backbone 这两个库,都没有采用 AMD 规范编写。如果要用 requireJS 加载它们的话,就必须先定义它们的特征。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require.config(
// shim 属性专门用来配置不兼容的模块
shim: {
'underscore': {
exports: '_' // 输出的变量名,表明这个模块外部调用时的名称
},
'backbone': {
deps: ['underscore', 'jquery'], // 该模块的依赖性
exports: 'backbone'
}
}
);

jQuery插件可以这样定义

1
2
3
4
5
6
7
8
9
require.config(
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
}
);

requireJS 插件

requireJS 还提供了一系列插件,实现特定的功能。比如:
domready 插件(回调函数在页面 DOM 结构加载完成后再运行)、
text 和 image 插件(允许 requireJS 加载文本和图片文件)、
json 和 mdown(用来加载 json 文件和 markdown 文件)等等。

其他

当依赖模块很多时…参数和模块一一对应,就太麻烦了,可以这样

1
2
3
4
5
6
7
8
9
10
11
define(function(require){
var dep1 = require('dep1'),
dep2 = require('dep2'),
dep3 = require('dep3'),
dep4 = require('dep4'),
dep5 = require('dep5'),
...;
//遍历文件夹,把每个文件都给require进来
//or 一个人自己的书单....
//or 一本书生成一个.js 想用的时候就require进来?但是它的接口是?
});

有人评论说:「define 后面跟着一长串的依赖感觉就像一场噩梦」,这个确实是个问题。我暂时的处理办法是通过 Sublime Text 插件来自动管理 AMD 依赖。当然这样只是让书写上更便捷,视觉上依赖数组一大坨还是不太美观。

//以上是异步加载

//下面是动态加载
http://javascript.ruanyifeng.com/tool/requirejs.html
可以根据 isReady 属性的值,决定下一步的动作

//服务器端采用 JSONP 模式,则可直接在 require 调用,来看下面的

1
2
3
4
5
6
7
8
9
10
11
require([
"http://someapi.com/foo?callback=define"
],
function(data){ //
console.log(data); // VS. ajax 呢??
},
function(err){ //处理错误的回调函数
});
//指定一个全局性的Error事件的监听函数
requirejs.onError = function(err){
};

参考

RequireJS 入门(一二)
http://www.cnblogs.com/snandy/archive/2012/05/22/2513652.html
http://www.cnblogs.com/snandy/archive/2012/05/23/2513712.html

Javascript模块化编程(三):require.js的用法
http://www.ruanyifeng.com/blog/2012/11/require_js.html

Q:
1.动态加载JS的应用场景:eg.当用户是否登录的时候-页面不同-加载的东东也就不同?eg.如果浏览器本身就支持 JSON 那就直接原生的,否则就用第三方的库
2.嗯~ 模块也可以是路径样子….Q:路径 和 模块名 它是如何查找的??
3.JSONP

文章目录
  1. 1. 简介
  2. 2. 解决了什么问题?
  3. 3. 如何使用?
    1. 3.1. 主页面
    2. 3.2. 主模块
    3. 3.3. 其他模块
    4. 3.4. 运行结果
  4. 4. AMD模块的写法
    1. 4.1. 不依赖其他模块
    2. 4.2. 依赖其他模块
    3. 4.3. jQuery
    4. 4.4. 加载非规范的模块
  5. 5. requireJS 插件
  6. 6. 其他
  7. 7. 参考