单独写一篇文章来阐述这类问题,是因为这个问题太容易犯错,并且太重要了。
对require加载的模块来说,它们都是单例的,但这个唯一标识取决于它们的模块名称及相对路径,与它们的绝对路径无关,换句话说,每个文件(模块)唯一性的辨别完全依赖与模块名称。
再强调一次,模块的唯一性与它们的访问路径无关,即使是地址完全相同的一份JS文件,如果引用的方式与模块的配置方式不一致,依旧会产生多个模块。
看下面的例子,假定目录结构如下:
require.js
* em/
* User.js
index.html
require的配置信息如下:
require.config({
baseUrl: './',
paths : {
'user' : './em'
}
})
User.js的文件内容如下:
define([], function() {
return {
username : 'yiifaa',
age : 20
};
});
下面看引用方式:
require(['user/User'], function(user) {
// 修改了User模块的内容
user.username = 'yiifee';
// em/User以baseUrl定义的模块进行访问
// 'user/User'以path定义的模块进行访问
require(['em/User', 'user/User'], function(u1, u2) {
// 输出的结果完全不相同,u1为yiifaa,u2为修改后的内容yiifee
console.log(u1, u2);
})
})
从上面的实验结果可以看出,即使是完全相同的一份文件,由于模块请求的方式差异,导致它们创建了两份实例,完全没有违反了AMD的设计原则。
项目问题1:requirejs的全局变量无效
在require的实际应用中,我们经常会创建一份文件用于全局变量,如果出现了全局变量初始化后依旧无效的问题,请仔细检查他们的请求方式是否完全依赖于同样的模块名(尤其是运用相对路径时)。
很神奇的地方在于,虽然模块不一致,但无论请求多少次require函数,网络请求依旧只会发送一份,即使添加了urlArgs参数,如下:
require.config({
// 其他配置信息
……
// 每次发送JS请求时,会自动添加参数后缀
urlArgs: "bust=" + (new Date()).getTime()
});
结论:AMD到底怎样引用模块
据我个人经验,要想保障AMD项目清晰明了,调用简单,最好基于以下原则:
1. 项目中的自编码模块最好基于根路径引用,这样在根路径下就会产生很多根模块,跨模块调用时最好基于根模块进行调用,
2. 如有困难或者需要改造,请优先定义packages,而不要使用paths定义自编码模块;
3. 如果非要使用paths,那就减弱或者消除packages对项目代码的使用;
4. 如果还不行,那就取消requie全局变量,改用window全局变量,但这样性能会有损耗。