说明:本文只提供快速入门内容,方便快速进入实战状态。更高级的配置,请参考官网文档。
当初之所以使用 RequireJS 等工具,是因为想提高js的加载速度,避免不必要的堵塞。但通过一段时间的使用,发现 RequireJS 更重要的作用是作为命名空间使用。
一、基本用法
RequireJS 官网文章不是中文的,篇幅不少(官方网址:http://requirejs.org)。网上的其他相关文章,有的讲的也很复杂。
其实,RequireJS 极其简单。如果你对 c# 或 java 语言的命名空间有一点点了解,那么,RequireJS 的用法和作用,可以用 c# 中的两行代码来类比说明:
using xx=wojilu.Core;
namespace MyApp {
public class MyClass {
public void MyTest() {}
}
}
RequireJS主要的功能,就是实现c#代码的第一行和第二行的功能:1)引入需要使用的命名空间(顺便加个别名也行);2)将自己的代码放到命名空间中,避免全局污染。
因为 javascript 天生的缺点,语言本身没有集成命名空间的概念,所以变量名、函数名很容易发生冲突。这些年,大家想尽了各种办法,给 js 添加命名空间的概念,其中最成熟的套路,就是 RequireJS 这种。
我们可以看到,在c#中只要两行代码的事情,在 js 中,得用多么复杂的类库,比如 RequireJS 去解决。
下面我们看一下 RequireJS 的具体写法,新建一个独立的 wojilu.test1.js 文件,然后输入如下代码:
define( ['wojilu.Core'], function(xx) {
return {
MyTest : function() { alert( 'wojilu1 module' );}
};
});
和上面的c#代码对比一下,RequireJS 同样也做了两件事情:
1、引入命名空间 wojilu.Core,同时给它取了一个别名 xx;
2、将自己的所有代码放在 define 中,避免全局化的污染冲突。
总之,RequireJS 定义了(define)一个命名空间,在定义的时候,顺便引用了需要使用其他命名空间。我们注意到,按照 RequireJS 的术语,它把命名空间叫做“模块”。注意,在这里,RequireJS 定义的模块(命名空间)是匿名的,没有取名,这是和c#不同的地方。
但这个不要紧,因为其他 js 在用到你这个 js 的时候,一般是根据文件名称加载的,同时可以通过上面的 xx 这种方式,给模块(命名空间)自定义别名,所以,取不取名,还真不是关键。
我们新建另外一个 wojilu.test2.js 文件,来使用 wojilu.test1.js:
define( ['wojilu.test1'], function( t1 ) {
return {
NewTest : function() { t1.MyTest();}
};
});
同样,也是通过文件名(不需要后缀名),引入了 wojilu.test1 命名空间,并给它取了别名 t1,然后在代码中使用 t1.MyTest() 方法。
按照 RequireJS 的规范,所有的模块定义,都必须放在 return {} 对象中。也就是说,你的代码都要写在 return 返回的 {} 对象里面。这会不会导致代码臃肿难看?当然不会。你可以重构一下,比如这样做:
define( ['wojilu.test1'], function( t1 ) { function someFunc1() {
// 实际主要代码
} function someFunc2() {
// 实际主要代码
}
// 通过 return 方式,将需要公开的函数暴露出来,供其他 js 调用
return {
NewTest : function() { t1.MyTest();},
fun1 : someFunc1,
fun2 : someFunc2
};
});
当然,要使用 RequireJS 的这些功能,你必须在页面中引用 require.js 文件
<script src="require.js"></script>
二、其他用法
上面的用法是标准用法,但 RequireJS 在加载其他 “模块/命名空间/js文件” 的时候,内部是通过传统的 <script src="some.js"></script> 来实现的,所以,你也可以把 RequireJS 仅仅当做 js 加载器使用。比如这行代码:
<script src="some.js"></script>
可以写成:
<script>require( ["some" ] );</script>
虽然仅仅是写法的变化,但带来了很多好处:
1、避免阻塞,提高了 js 的加载性能;
2、通过编程使用 require 方法加载,而不是<script>硬写,更加灵活。
三、基本配置
上面使用 RequireJS 的方式是这样处理的:
<script src="require.js"></script>
这种写法虽然简单,但其实并不推荐,一般的写法还要再加个属性:
<script data-main="js/main" src="js/require-jquery.js"></script>
就像一个c程序总有一个 main 方法作为入口一样,上面这种写法,做了几件事情:
1、加载了 require-jquery.js 文件。注意,官方提供了 RequireJS 和 jquery 的打包版本,推荐。
2、在加载之后,加载入口文件 js/main.js ,注意,main.js 写进去的时候,不需要后缀名。
你的所有其他 js 模块文件,都可以写在 main.js 里面,通过 main.js 加载。
四、加载的用法
到此为止,我们遇到了两个关键词,一个是 define ,可以用来定义模块(命名空间),第一部分我们讲了;还有一个是 require,可以直接加载其他 js。它除了简单的用法:
<script>
require( ["some" ] );
</script>
之外,还有和 define 类似的复杂用法:
<script>
require(["aModule", "bModule"], function() {
myFunctionA(); // 使用 aModule.js 中的函数 myFunctionA
myFunctionB(); // 使用 bModule.js 中的函数 myFunctionB
});
</script>
总结一下,define 是你定义自己的模块的时候使用,可以顺便加载其他js;require 直截了当,供你加载用的,它就是一个加载方法,加载的时候,可以定义别名。
五、在 RequireJS 之前运行
有了 RequireJS 这个工具,我们可以将网页 <head> 中的所有 js 都挪到页脚,以提高 js 的加载速度。请在 www.wojilu.com 网页上右键查看 html 源码,看一下实际效果。
理论上,js 脚本都应该放到独立的 js 文件中,而不是直接嵌入网页内联执行,好处是方便浏览器缓存,不用每次都加载页面内容。但只要你不是有真正的代码洁癖,总有一些特殊时候,需要在加载 require.js 之前,运行一些script脚本。这时候,你可以使用 wojilu 目前的做法,把所有的代码都放在一个叫 _run 的函数中,比如在 require.js 加载代码上面一行:
<script>
_run(function () {
require(['wojilu.core.base'], function (x) { x.customSkin().backTop(); });
});
</script>
这行代码引用了 wojilu.core.base 文件,但因为是放在 _run 中的,所以并不是立刻执行,而是延迟到 require.js 加载之后执行。 run 表示运行,前面加上下划线,表示“延迟”运行。
至于wojilu如何做到这一点的,请自己阅读源码,其实思路是很简单的。
六、版本号
为了在js更新之后,及时通知客户端刷新缓存,我们一般需要给js加上版本号,具体说明,参看这里:http://www.wojilu.com/Common/Page/13 。在 RequireJS 中也可以给所有需要加载的 js 加上版本号,方法是给 require.js 提供一个配置文件。请在 requrie.js 加载之前,提供一个全局变量 require:
<script>
var require = {
urlArgs: 'v=版本号'
};
</script>
<script src="scripts/require.js"></script>
注意:不要把 require 变量写成 window.require ,据说在IE下会有问题。
RequireJS 还提供了其他丰富的配置选项,具体请访问官网查看。
七、其他问题
1、路径与后缀名
在 require 一个 js 文件的时候,一般不需要加上后缀名。如果加上后缀名,会按照绝对路径加载。没有后缀名,是按照下面的路径加载:
<script data-main="js/main" src="js/require-jquery.js"></script>
也就是默认加载 data-main 指定的目录,即 js/main.js 文件所在的目录。当然,你可以通过配置文件修改。
2、define 定义模块方法只能用在独立的js文件中,不能在页面中直接使用。
否则会报 Mismatched anonymous define() module 错误。
3、和其他第三方js类库是否冲突?
不会冲突。一般比较规范的类库,都会给自己的js加上命名空间。比如 wojilu 旧有的 wojilu.common.js ,其实就是放在 wojilu 命名空间中(当然是通过更原始的方式实现命名空间的)。
在通过 RequireJS 加载这些第三方的 js 的时候,完全不要有任何担忧。
当然,如果第三方类库能够使用 RequireJS 的方式进行改造,那是最好。比如 wojilu 中大多数js 都按照 RequireJS 的方式进行了改造。但是,如果你不改造,也是完全不要紧的。
4、在代码中 require 一个文件多次,是否会导致浏览器反复加载?
不会,这是 RequrieJS 的优点,即使你反复 require 它,它只加载一次。