requireJS使用shim注入非标准模块详解

在javascript中定义全局变量有2种方式,本质上是等价的,都是向window对象注入属性或者方法。javascript

 

// global.js

var g_name = "aty";

window.g_age = 25;

当global.js加载的时候,浏览器的全局对象window就会多出2个属性:g_name和g_age。html

 

咱们编写一个js工具类或者是js框架,一般有2种方式:
方式1:dateUtil.jsjava

 

(function(window) {
 
	var DateUtils = {};
	
	DateUtils.toString = function(){
		alert("toString");
	};
 
	// 全局变量
	window.DateUtils = DateUtils;
  
})(window);

这种方式是最经常使用的,好比JQuery、Underscore等框架都是采用这种结构编写的。

数组

 

方式2:stringUtil.js浏览器

 

// 全局变量
var StringUtils = {};


StringUtils.toUpperCase = function(input){
	alert("toUpperCase");
}


很显然stringUtil.js和dateUtil.js都不符合AMD规范,如今咱们看看如何经过requireJS进行加载。工程目录结构以下:框架

index.html
main.js
libs
	--dateUtil.js
	--stringUtil.js

 

index.html和main.js的实现代码以下:函数

 

<!doctype html>
<html>
    <head>
        <title>shim</title>
        <meta charset="utf-8">
        <script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
    </head>
    <body>
		<div id="div1" style="width:200px;height:200px;"></div>
     
    </body>
</html>

 

requirejs.config({
    baseUrl: 'libs'
});


require(["dateUtil","stringUtil"], function(dateUtil,stringUtil) {
    
	alert(dateUtil===undefined);//true

});


运行index.html,经过F12观察:工具

 

技术分享

很明显dateUtil.js和stringUtil.js可以被requireJS正常加载,可是不能获取到这2个模块的返回值。咱们修改下index.html,给div注册事件处理函数,并在事件处理函数中调用stringUtil.js提供的方法:requirejs

 

<!doctype html>
<html>
    <head>
        <title>shim</title>
        <meta charset="utf-8">
        <script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
		<script>
			function test()
			{
				StringUtils.toUpperCase();
			}
		</script>
    </head>
    <body>
		<div id="div1" style="width:200px;height:200px;" onclick="test();"></div>
     
    </body>
</html>

点击div1,能够发现test()函数不会报错。也就是说,requireJS加载不符合AMD规范的js文件,跟咱们直接在html经过<script>标签加载,没有太大的差异。js文件中引入的全局变量,依然会存在,依然可以正常使用。ui

 

 

下面咱们看下shim参数的使用方式,咱们将main.js修改以下:

 

requirejs.config({
    baseUrl: 'libs',
	shim:{
		dateUtil:{
			  deps:[],
			  exports: 'DateUtils'
		},
		stringUtil:{
			  deps:[],
			  exports: 'StringUtils'
		}
	}
});


require(["dateUtil","stringUtil"], function(dateUtil,stringUtil) {
    
	stringUtil.toUpperCase();
	dateUtil.toString();

});

这段代码能够正常运行,能够看到:shim参数可以帮助咱们以AMD模块的方式,使用那些不符合AMD规范的模块。下面接介绍下:deps和exports的含义。exports很好理解,就是模块的返回值。main.js中exports的值,必定要与dateUtil.js和stringUtil.js中暴露出的全局变量名称一致。很显然dateUtil.js和stringUtil.js这2个模块的返回值,就是暴露出的全局变量window.DateUtils和window.StringUtils,requireJS框架就是将这些全局变量的值返回,做为模块的返回结果。若是dateUtil.js或stringUtil.js中暴露了多个全局变量,那么exports能够指定其中任何的一个,做为模块的返回结果。不过通常的框架,都只会使用1个全局变量,这样冲突的可能性会减小,毕竟全局变量越少越好。

 

上面咱们编写的dateUtil.js和stringUtil.js,都不依赖于其余js模块,因此指定的deps是空数组。下面咱们编写的aplugin.js和bplugin.js都依赖于模块util.js。

//aplugin.js
(function(window,util) {
 
	var a = {};
	
	a.toString = function(){
		alert("a="+util.add(1,2));
	};
 
	// 全局变量
	window.a = a;
  
})(window,util);
//bplugin.js

var b = {};

b.toString = function(){
	alert("b="+util.add(1,2));
}
//util.js
var util = {};

util.add = function(v1,v2){
	return v1+v2;
};

main.js代码以下,只有设置正确的依赖顺序,使用的时候才不会出问题。

 

 

requirejs.config({
    baseUrl: 'libs',
	shim:{
		dateUtil:{
			  deps:[],
			  exports: 'DateUtils'
		},
		stringUtil:{
			  deps:[],
			  exports: 'StringUtils'
		},
		aplugin:{
			  deps:["util"],
			  exports: 'a'
		},
		bplugin:{
			  deps:["util"],
			  exports: 'b'
		}
	}
});


require(["stringUtil","dateUtil","aplugin","bplugin"], function(string,date) {
    
	//string.toString();
	//date.toString();
	var aPl = require("aplugin");
	var bPl = require("bplugin");
	aPl.toString();
	bPl.toString();

});

很显然util.js也不符合AMD规范,若是A模块依赖于B模块,A模块不符合AMD规范(使用的是全局变量),那么B模块也必须是使用全局变量,不然会报错。即若是将util.js改为符合AMD规范的写法,那么aplugin.js和bplugin.js都会因找不到util对象而报错。

 

 

// 符合AMD规范的util.js

define(function(){
	
	function add(v1,v2)
	{
		return v1+v2;
	}
	
	return {"add":add};

});


最后咱们看下shim配置参数中init的做用。init能够指定一个函数主要就是用来避免类库之间的冲突。因为不符合AMD规范的js文件,会使用全局变量。因此当加载多个模块的时候存在名字冲突的可能。好比JQuery、UnderScore等框架都会提供一个noConflict()函数来避免名字冲突,noConflict()的实现原理能够参考这篇文章

 

咱们编写一个不符合AMD规范的模块conflict.js,使用了全局变量$E,并提供noConflict方法。

(function(window) {
	// 保存以前数据
	var _$E = window.$E;

	var myplugin = {"name":"aty"};
	myplugin.noConflict = function(){
		window.$E = _$E;
		return myplugin;
	};
	
	// 向全局对象注册$E
	window.$E = myplugin;
})(window);


将index.html修改以下,在requireJS加载以前,先定义一个全局变量$E。

 

 

<!doctype html>
<html>
    <head>
        <title>shim</title>
        <meta charset="utf-8">
		<script>
			var $E = "before";
		</script>
        <script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
    </head>
    <body>
		<div id="div1" style="width:200px;height:200px;" onclick="test();"></div>
    </body>
</html>


main.js中代码以下:

 

requirejs.config({
    baseUrl: 'libs',
	shim:{
		conflict:{
			  deps:[],
			  exports: '$E',
			  init:function(){
				 return $E.noConflict();
			  }
		}
	}
});


require(["conflict"], function(mayConflict) {
    
	alert(mayConflict.name);
	
	alert(window.$E);//before
	
});

运行index.html,能够发现conflict.js可以与以前定义的全局变量$E共存,避免了冲突,这就是经过init实现的。若是没有定义init,能够看到alert(window.$E)打印的值是undefined。

原文:http://blog.csdn.net/aitangyong/article/details/44225859

相关文章
相关标签/搜索