JavaScript中Require调用js的实例分享
在我最初开始写JavaScript函数时,通常是这样的:
functionfun1(){ //somecodehere } functionfun2(){ //someothercodehere } ...
函数全写在全局环境中,项目很小时,通常不会有什么冲突问题。
但代码多了后,渐渐就发现,函数名称(英文词汇)有点不够用了。于是引入命名空间的概念,开始模块化代码。
命名空间下的函数
在命名空间下,我的代码这样写:
varcom=com||{}; com.zfanw=com.zfanw||{}; com.zfanw.module1=(function(){ //somecodehere return{ func1:func1, ... }; }()); com.zfanw.module2=(function(){ //someothercodehere return{ func1:func1, ... }; }()); ...
本着要面向对象的原则,执行函数通常我要这么写的:
com.zfanw.module1.func1.apply({},['arg1',arg2]); ...
当然,为了少打些字符,我还会在闭包中导入1公共API接口:www.nhooo.com
(function($,mod1){ //somecodehere mod1.func1.apply({},['arg1',arg2]); }(jQuery,com.zfanw.module1)); ...
至此,代码冲突的可能性已经很小,但代码依赖的问题,多脚本文件管理、阻塞的问题,渐渐浮出水面–命名空间的办法开始捉急。
于是Require.js2出场。
Require.js
首先了解下require.js里模块的概念3:
Amoduleisdifferentfromatraditionalscriptfileinthatitdefinesawell-scopedobjectthatavoidspollutingtheglobalnamespace.Itcanexplicitlylistitsdependenciesandgetahandleonthosedependencieswithoutneedingtorefertoglobalobjects,butinsteadreceivethedependenciesasargumentstothefunctionthatdefinesthemodule.
简单地说,有两点,一、模块作用域自成一体,不污染全局空间;二、模块指明依赖关系,并且依赖是通过参数传递的形式导入的,无需通过全局对象引用–依赖同样不污染全局空间。
定义模块
与上面的老长的命名空间方式不同,require.js用全局方法define来定义模块,形式如下:
define(id?,dependencies?,factory);//?表示可选项
我且把模块分两种。
无依赖的模块
假如一个模块并不依赖其他模块,那么定义起来是很简单的,比如模块hello放在hello.js文件中:
define(function(){ //somecodehere return{ //somepublicapi }; });
有依赖的模块
有依赖的模块要稍稍复杂点,define时,我们需要罗列出模块的依赖情况:
define(['jquery'],function($){//比如这个模块,代码的执行依赖jQuery,require.js会先加载jquery模块代码,并加以执行,然后将依赖模块以$的参数形式传入回调函数中,回调函数将执行结果注册为模块 //maybesomecodehere return{ //somepublicapi }; });
这里,依赖中的'jquery'是模块相对于baseUrl的路径,等效于模块ID。
现在,再回过头,看看上面写过的闭包中导入公共API的代码,跟define函数做个对比:
(function($,mod1){ //somecodehere mod1.func1.apply({},['arg1',arg2]); }(jQuery,com.zfanw.module1));
这段代码里,我同样把jQuery导入了,在闭包里,我同样是通过$这个外部传入的参数来访问jQuery。可以说,它「定义依赖」的方式跟define方法很相似,不同的是,define导入的jquery不是全局变量,所以不会污染全局环境。
关于模块名称
define函数有三个参数,第一个id即模块名称,这个名称的格式是相对于baseUrl的路径除去文件格式,比如baseUrl为js目录,一个模块放在js/libs/hi.js里,则如果名称是这样定义的:
define('libs/hi',['jquery'],function($){......});
这样的定义形式的好处是,模块不可能冲突,因为同一目录下不允许同名文件。但也因此require.js建议我们不要设置模块名称,因为设置了‘libs/hi'的模块名称后,模块就必须放在js/libs目录下的hi.js文件中,要移动位置的话,模块名称要跟着改变。至于后期利用r.js优化时生成了模块名称,那已经是另外一回事。
使用模块
在定义好「有依赖」、「没依赖」的各种模块后,我们该怎么用它?Require.js提供了一个函数,require(与requirejs等效)。
require函数加载依赖并执行回调,与define不同的是,它不会把回调结果4注册成模块:
require(['jquery'],function($){//这个函数加载jquery依赖,然后执行回调代码 console.log($); });
举一个简单的例子。我有一个文件夹,文件结构如下:
index.html js/ main.js require.js jquery.js
这里jquery.js已经注册为AMD模块,则HTML文件里这样引用require.js:
require.js会检查data-main属性值,这里是js/main,根据设定,它会加载js目录下的main.js文件。
main.js文件里,我只干一件事,用jQuery方法取得当前窗口的宽度:
require(['jquery'],function($){ varw=$(window).width(); console.log(w); });
执行代码就这么简单。
非AMD规范的模块
但事情远没有我们想像中那么美好,AMD只是一种社区规范,并非标准,而且在它出现以前,已经有各种各样的流行库存在,更不用说我们自己早期写的代码,所以我们一定会碰上一堆非AMD规范的模块。为了让require.js能够加载它们,并且自动识别、载入依赖,我们有两种选择,一、给它们全穿上一个叫define的函数;二、使用Require.js提供的配置选项shim,曲线救国。
比如我手上一个项目,因为某种原因,还在用jQuery1.4.1版本,而jQuery是从1.7版本开始才注册为AMD模块的,我要在require.js中使用,就需要先做shim:
require.config({ shim:{ 'jquery-1.4.1':{//<=这个是相对于main.js的路径www.45it.com exports:'jQuery'//<=这个值需要稍加注意,稍后会提及 }, 'libs/jquery-throttle-debounce.min':{//<=jQuery插件 deps:['jquery-1.4.1']//无需exports,因为我们只是在增强jQuery功能 } }, }); require(['jquery-1.4.1','libs/jquery-throttle-debounce.min'],function($){ console.log($.debounce); });
写完shim,发现jquery-1.4.1、libs/jquery-throttle-debounce.min这样的名称有点长。这里我们又有两种选择,一是直接打开修改js文件名,或者使用require.js提供的配置项paths给模块ID指定对应的真实文件路径:
require.config({ paths:{ 'jquery':'jquery-1.4.1',//<=模块jquery指向js/jquery-1.4.1.js文件 'debounce':'libs/jquery-throttle-debounce.min' }, shim:{ 'jquery':{ exports:'$' }, 'debounce':{ deps:['jquery'] } } }); require(['jquery','debounce'],function($){ console.log($.debounce); });
这样,引用起来就方便多了。
另外,需要注意shim中的exports项,它的概念更接近imports,即把全局变量导入。我们如果把exports值改成非全局变量名,就会导致传入回调的对象变成undefined,举个例子:
require.config({ paths:{ 'jquery':'jquery-1.4.1', }, shim:{ 'jquery':{ exports:'hellojQuery'//这里我把exports值设置为jQuery/$以外的值 } } }); require(['jquery'],function($){ console.log($);//这里,会显示undefined });
其他模块在做shim时同理,比如underscore需要exports成_。
Require.js的好处
说了这么多,Require.js到底有什么好处?
并行加载
我们知道,标签会阻塞页面,加载a.js时,后面的所有文件都得等它加载完成并执行结束后才能开始加载、执行。而require.js的模块可以并行下载,没有依赖关系的模块还可以并行执行,大大加快页面访问速度。
不愁依赖
在我们定义模块的时候,我们就已经决定好模块的依赖–c依赖b,b又依赖a。当我想用c模块的功能时,我只要在require函数的依赖里指定c:
require(['c'],function(c){...});
至于c依赖的模块,c依赖的模块的依赖模块…等等,require.js会帮我们打理。
而传统的script办法,我们必须明确指定所有依赖顺序:
换句话说,传统的script方法里,我们极可能要靠记忆或者检查模块内容这种方式来确定依赖–效率太低,还费脑。
减少全局冲突
通过define的方式,我们大量减少了全局变量,这样代码冲突的概率就极小极小–JavaScript界有句话说,全局变量是魔鬼,想想,我们能减少魔鬼的数量,我想是件好事。
关于全局变量
有一点需要说明的是,require.js环境中并不是只有define和require几个全局变量。许多库都会向全局环境中暴露变量,以jQuery为例,1.7版本后,它虽然注册自己为AMD模块,但同时也向全局环境中暴露了jQuery与$。所以以下代码中,虽然我们没有向回调函数传入一份引用,jQuery/$同样是存在的:
require(['jquery'],function(){ console.log(jQuery); console.log($); });
以上这篇JavaScript中Require调用js的实例分享就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。