前些日子写了一篇关于腾讯模板引擎TmodJS的文章《模板引擎artTemplate及模板预编译器TmodJS的使用入门》,算是对其原理与使用进行了初步的接触与研究。近期在一个项目中对TmodJS进行了尝试,不曾想无心中竟然发现了TmodJS与百度图表神器Echarts之间存在一个看似不能共存的冲突。html
问题是这样的:项目的某个页面中同时显式地引入了Echarts的主文件“echarts.js”以及TmodJS的依赖文件“require.js”。当执行到TmodJS所需的require()方法时,脚本会报“Error: [REQUIRE_FATAL]Relative ID is not allowed in global require”的错误,进而致使模板没法被加载以及渲染。报错如图所示:数组
从报错信息上看,问题存在于echarts以及require方法上。这个项目中的Echarts均采用模块化方式进行加载,而TmodJS也须要经过“require.js”来实现编译后模板的加载以及后续的DOM渲染。但前者内置的是百度本身实现的模块化加载器“esl”,由于此前对echarts的实现有着一些粗浅的了解,所以我认为问题颇有可能和esl抛出的require方法与require.js抛出的同名方法不兼容相关。浏览器
因为页面中先引入了esl,其在加载成功以后便在window对象中留下了全局方法require和define。而随后引入的require.js在加载时因为检测到了上述两个方法的存在,则认为自身已有实例存在而不会继续进行加载(若继续加载则会覆盖esl的两个同名方法)。require.js的相关代码以下:echarts
虽然二者抛出方法的名称一致,但它们所需参数的内容却并不相同。二者require方法所需的第一个参数均为array类型,但require.js须要的array中直接存放的就是模块相对路径的字串。而esl须要的参数更为复杂一些,其先要经过require.config方法,将相对路径的字串设置到一个object中的一个名为paths的object的指定key中,然后在使用其本身定义的require时,会将以前指定的key依次压入array中并将这个array做为第一个参数。二者在require方法所需参数上的差别,最终致使了上述脚本错误的出现。看似两个风马牛不相及的东西,竟然由于模块化加载器之间的冲突产生了交集,确实让人小意外了一下。模块化
既然问题的缘由已经肯定,那么咱们就能够尝试进行解决了。个人解决思路是,默认在页面中不显式引入require.js。在TmodJS须要使用require方法以前,先对当前浏览器环境的require进行断定。若是加载了esl,则将模块相对路径按照esl须要的方式配置、转化,进而经过esl加载。若是已存在自身或其它amd加载器抛出的require方法,则直接调用。若是干脆就不存在require方法,则动态地加载require.js。具体代码以下:函数
// 代码简陋,还请各位路过的高手见谅。若有疏漏还请海涵、指正
function NeedReq(arg){ // 参数形如{
list:{ // 指定模块的名称和相对路径
'tmpl':'../../js/template'
},
callback:function(template){ // 指定require方法加载以后的回调
// some code...
},
reqPath:'../../js/require.js' // 指定require.js的相对路径
}ui
// description: 智能判断页面是否加载了esl并由此决定是否加载require.js
// author: Liyanyang(gudaozr)
// version: 1.1spa
var reqPath = [], reqRelativePath = [], reqCallback = null; // 分别定义esl、require.js所用参数数组以及require.js动态加载成功后制定的回调函数
var objIsEmpty = function(obj){ // 检测对象是否为空的函数,这里主要用于判断传进来的参数是否有效
if(typeof obj != 'object'){
return false;
}
for(var key in obj){
return false;
}
return true;
};
if(!arguments.length){ // 参数有效性、完整性判断,不符合直接pass~
return false;
}else if(!arg.list || objIsEmpty(arg.list)){
return false;
}else if(typeof arg.callback != 'function'){
return false;
}else if(!arg.reqPath){
return false;
}
var i = 0;
for(var key in arg.list){ // 按照esl和require.js需求生成不一样内容形式的参数数组
reqPath[i] = key;
reqRelativePath[i++] = arg.list[key];
}
reqCallback = arg.callback;
if(window.require && window.require.loader == 'esl'){ // esl已经加载,不须要require.js
require.config({
paths:arg.list
});
require(reqPath,reqCallback);
}else if(!window.require){ // esl和require.js均未加载,须要加载require.js
var scrNode = document.createElement('script');
scrNode.setAttribute('src',arg.reqPath);
var scripts = document.getElementsByTagName('script');
if(scripts.length){
var scrParent = scripts[0].parentNode;
if(scripts.length == 1){
scrParent.insertBefore(scrNode,scripts[0]);
}else{
scrParent.insertBefore(scrNode,scripts[1]);
}
scrNode.onload = function(){
require(reqRelativePath,reqCallback);
}
}
}else if(window.require && define.amd){ // require.js或者其它amd加载器已经加载
require(reqRelativePath,reqCallback);
}
return true;
}code
寥寥草草记录了下这个问题的发现以及解决,真心但愿可以帮到遇到同类问题的朋友。若是文章中存在什么错误或者不周之处,还请各位高手批评、指正。期待与你们进行交流!htm