requirejs中define和require的定位以及使用区别?

define定义require加载使用,define定义的模块可以被其它调用 require不行.

define和require在依赖处理和回调执行上都是一样的,不一样的地方是define的回调函数需要有return语句返回模块对象,这样define定义的模块才能被其他模块引用;require的回调函数不需要return语句

define([require,path/myMod1,path/moMod2], function(require){  var mod1 = require('path/myMod1'), mod2 = require('path/myMod2');
})其实逻辑上类似于define([require], function(require){  var mod1 = require('path/myMod1'), mod2 = require('path/myMod2');
})你只是把参数丢了而已,所以这样也行define([require,path/myMod1,path/moMod2], function(require){  var mod1 = arguments[1], mod2 = arguments[2];
})所以,可以这么用define([require,path/myMod1,path/moMod2], function(require,mod1,mod2){  // 这里 mod1 和 mod2 都准备好了,还可以用require继续加载别的
})或者,改成你看到的那些例子define([path/myMod1,path/moMod2], function(mod1,mod2){
})所以define(['jquery.validate'], function(validate){// 这里可以用validate,但是用不了jquery了?
})这样本质上并没有问题,只不过你引用了这个插件,必然也是想用jquery的而不只是validate,所以define(['jquery', 'jquery.validate'], function(jquery, validate){// 这样才有jquery
})像你一开始那样使用也行define(['require', 'jquery.validate'], function(require, validate){var jquery = require('jquery');
})

其实这里又要扯到AMD和CMD的区别,依赖前置和依赖就近。

define([path/myMod1,path/moMod2], function(mod1,mod2){
})

是依赖前置。

define(function(require, exports, module) {var $ = require('jquery');
})

这是依赖后置。
后者可以用来解决循环引用问题。。

第二个问题: 关于requirejs.config 中shim
题主理解有点偏差,requirejs.config 中shim作用是 为那些没有使用define()来声明依赖关系、设置模块的"浏览器全局变量注入"型脚本做依赖和导出配置。
简单来说就是对requirejs要引用的第三方非AMD规范的插件、类库的特殊处理。通常有两种方法:
A: AMD化.通过define封装一下
B: config shim.
另外使用seajs的时候也要注意非CMD插件的CMD化。

补充:require和define区别。使用方法一样

define(['./jquery'], function($) { console.log($); });
require(['./jquery'], function($) { console.log($); });

都能加载到jquery. 但是define与require不同的就是它多出来一个接口的返回。所以当把define写成require后你会发现别的module引用不到本次定义的module.

使用例子

requirejs.config({baseUrl: 'libs',paths: {"core1": 'cores/core1',"core2": 'cores/core2',"util1": 'utils/util1',"util2": 'utils/util2',"service1": 'services/service1',"service2": 'services/service2',}
});require(["core1","core2","util1","util2","service1","service2"], function() {var core1 = require("core1");var core2 = require("core2");var util1 = require("util1");var util2 = require("util2");var service1 = require("service1");var service2 = require("service2");});
  1. 声明不同js文件之间的依赖
  2. 可以按需、并行、延时载入js库
  3. 可以让我们的代码以模块化的方式组织

初看起来并不复杂。

在html中引入requirejs

在HTML中,添加这样的 <script> 标签:

<script src="/path/to/require.js" data-main="/path/to/app/config.js"></script>

通常使用requirejs的话,我们只需要导入requirejs即可,不需要显式导入其它的js库,因为这个工作会交给requirejs来做。

属性 data-main 是告诉requirejs:你下载完以后,马上去载入真正的入口文件。它一般用来对requirejs进行配置,并且载入真正的程序模块。

在config.js中配置requirejs

config.js 中通常用来做两件事:

  1. 配置requirejs 比如项目中用到哪些模块,文件路径是什么
  2. 载入程序主模块
requirejs.config({baseUrl: '/public/js',paths: {app: 'app'}
});requirejs(['app'], function(app) {app.hello();
});

在 paths 中,我们声明了一个名为 app 的模块,以及它对应的js文件地址。在最理想的情况下, app.js 的内容,应该使用requirejs的方式来定义模块:

define([], function() {return {hello: function() {alert("hello, app~");}}
});

这里的 define 是requirejs提供的函数。requirejs一共提供了两个全局变量:

  1. requirejs/require: 用来配置requirejs及载入入口模块。如果其中一个命名被其它库使用了,我们可以用另一个
  2. define: 定义一个模块

另外还可以把 require 当作依赖的模块,然后调用它的方法:

define(["require"], function(require) {var cssUrl = require.toUrl("./style.css");
});

依赖一个不使用requirejs方式的库

前面的代码是理想的情况,即依赖的js文件,里面用了 define(...) 这样的方式来组织代码的。如果没用这种方式,会出现什么情况?

比如这个 hello.js :

function hello() {alert("hello, world~");
}

它就按最普通的方式定义了一个函数,我们能在requirejs里使用它吗?

先看下面不能正确工作的代码:

requirejs.config({baseUrl: '/public/js',paths: {hello: 'hello'}
});requirejs(['hello'], function(hello) {hello();
});

这段代码会报错,提示:

Uncaught TypeError: undefined is not a function

原因是最后调用 hello() 的时候,这个 hello 是个 undefined . 这说明,虽然我们依赖了一个js库(它会被载入),但requirejs无法从中拿到代表它的对象注入进来供我们使用。

在这种情况下,我们要使用 shim ,将某个依赖中的某个全局变量暴露给requirejs,当作这个模块本身的引用。

requirejs.config({baseUrl: '/public/js',paths: {hello: 'hello'},shim: {hello: { exports: 'hello' }}
});requirejs(['hello'], function(hello) {hello();
});

再运行就正常了。

上面代码 exports: 'hello' 中的 hello ,是我们在 hello.js 中定义的hello 函数。当我们使用 function hello() {} 的方式定义一个函数的时候,它就是全局可用的。如果我们选择了把它 export 给requirejs,那当我们的代码依赖于hello 模块的时候,就可以拿到这个 hello 函数的引用了。

所以: exports 可以把某个非requirejs方式的代码中的某一个全局变量暴露出去,当作该模块以引用。

暴露多个变量:init

但如果我要同时暴露多个全局变量呢?比如, hello.js 的定义其实是这样的:

function hello() {alert("hello, world~");
}
function hello2() {alert("hello, world, again~");
}

它定义了两个函数,而我两个都想要。

这时就不能再用 exports 了,必须换成 init 函数:

requirejs.config({baseUrl: '/public/js',paths: {hello: 'hello'},shim: {hello: {init: function() {return {hello: hello,hello2: hello2}}}}
});requirejs(['hello'], function(hello) {hello.hello1();hello.hello2();
});

当 exports 与 init 同时存在的时候, exports 将被忽略。

无主的与有主的模块

我遇到了一个折腾我不少时间的问题:为什么我只能使用 jquery 来依赖jquery, 而不能用其它的名字?

比如下面这段代码:

requirejs.config({baseUrl: '/public/js',paths: {myjquery: 'lib/jquery/jquery'}
});requirejs(['myjquery'], function(jq) {alert(jq);
});

它会提示我:

jq is undefined

但我仅仅改个名字:

requirejs.config({baseUrl: '/public/js',paths: {jquery: 'lib/jquery/jquery'}
});requirejs(['jquery'], function(jq) {alert(jq);
});

就一切正常了,能打印出 jq 相应的对象了。

为什么?我始终没搞清楚问题在哪儿。

有主的模块

经常研究,发现原来在jquery中已经定义了:

define('jquery', [], function() { ... });

它这里的 define 跟我们前面看到的 app.js 不同,在于它多了第一个参数'jquery' ,表示给当前这个模块起了名字 jquery ,它已经是有主的了,只能属于jquery .

所以当我们使用另一个名字:

myjquery: 'lib/jquery/jquery'

去引用这个库的时候,它会发现,在 jquery.js 里声明的模块名 jquery 与我自己使用的模块名 myjquery 不能,便不会把它赋给 myjquery ,所以 myjquery 的值是 undefined 。

所以我们在使用一个第三方的时候,一定要注意它是否声明了一个确定的模块名。

无主的模块

如果我们不指明模块名,就像这样:

define([...], function() {...
});

那么它就是无主的模块。我们可以在 requirejs.config 里,使用任意一个模块名来引用它。这样的话,就让我们的命名非常自由,大部分的模块就是无主的。

为什么有的有主,有的无主

可以看到,无主的模块使用起来非常自由,为什么某些库(jquery, underscore)要把自己声明为有主的呢?

按某些说法,这么做是出于性能的考虑。因为像 jquery , underscore 这样的基础库,经常被其它的库依赖。如果声明为无主的,那么其它的库很可能起不同的模块名,这样当我们使用它们时,就可能会多次载入jquery/underscore。

而把它们声明为有主的,那么所有的模块只能使用同一个名字引用它们,这样系统就只会载入它们一次。

挖墙角

对于有主的模块,我们还有一种方式可以挖墙角:不把它们当作满足requirejs规范的模块,而当作普通js库,然后在 shim 中导出它们定义的全局变量。

requirejs.config({baseUrl: '/public/js',paths: {myjquery: 'lib/jquery/jquery'},shim: {myjquery: { exports: 'jQuery' }}
});requirejs(['myjquery'], function(jq) {alert(jq);
});

这样通过暴露 jQuery 这个全局变量给 myjquery ,我们就能正常的使用它了。

不过我们完全没有必要这么挖墙角,因为对于我们来说,似乎没有任何好处。

如何完全不让jquery污染全局的$

在前面引用jquery的这几种方式中,我们虽然可以以模块的方式拿到jquery模块的引用,但是还是可以在任何地方使用全局变量 jQuery 和 $ 。有没有办法让jquery完全不污染这两个变量?

在init中调用noConflict (无效)

首先尝试一种最简单但是不工作的方式:

requirejs.config({baseUrl: '/public/js',paths: {jquery: 'lib/jquery/jquery'},shim: {jquery: {init: function() {return jQuery.noConflict(true);}}}
});requirejs(['jquery'], function(jq) {alert($);
});

这样是不工作的,还是会弹出来一个非 undefined 的值。其原因是,一旦requirejs为模块名 jquery 找到了属于它的模块,它就会忽略 shim 中相应的内容。也就是说,下面这段代码完全没有执行:

jquery: {init: function() {return jQuery.noConflict(true);}
}

使用另一个名字

如果我们使用挖墙角的方式来使用jquery,如下:

requirejs.config({baseUrl: '/public/js',paths: {myjquery: 'lib/jquery/jquery'},shim: {myjquery: {init: function() {return jQuery.noConflict(true);}}}
});requirejs(['myjquery'], function(jq) {alert($);
});

这样的确有效,这时弹出来的就是一个 undefined 。但是这样做的问题是,如果我们引用的某个第三方库还是使用 jquery 来引用jquery,那么就会报“找不到模块”的错了。

我们要么得手动修改第三方模块的代码,要么再为它们提供一个 jquery 模块。但是使用后者的话,全局变量 $ 可能又重新被污染了。

使用map

如果我们有办法能让在继续使用 jquery 这个模块名的同时,有机会调用jQuery.noConflict(true) 就好了。

我们可以再定义一个模块,仅仅为了执行这句代码:

jquery-private.js

define(['jquery'], function(jq) {return jQuery.noConflict(true);
});

然后在入口处先调用它:

requirejs.config({baseUrl: '/public/js',paths: {jquery: 'lib/jquery/jquery','jquery-private': 'jquery-private'}
});requirejs(['jquery-private', 'jquery'], function() {alert($);
});

这样的确可行,但是还是会有问题: 我们必须小心的确保 jquery-private 永远是第一个被依赖,这样它才有机会尽早调用 jQuery.noConflict(true) 清除全局变量 $ 和 jQuery 。这种保证只能靠人,非常不可靠。

我们这时可以引入 map 配置,一劳永逸地解决这样问题:

requirejs.config({baseUrl: '/public/js',paths: {jquery: 'lib/jquery/jquery','jquery-private': 'jquery-private'},map: {'*': { 'jquery': 'jquery-private'},'jquery-private': { 'jquery': 'jquery'}}
});requirejs(['jquery'], function(jq) {alert($);
});

这样做,就解决了前面的问题:在除了jquery-private之外的任何依赖中,还可以直接使用 jqurey 这个模块名,并且总是被替换为对 jquery-private 的依赖,使得它最先被执行。

requireJs详解(二)相关推荐

  1. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  2. [转]文件IO详解(二)---文件描述符(fd)和inode号的关系

    原文:https://www.cnblogs.com/frank-yxs/p/5925563.html 文件IO详解(二)---文件描述符(fd)和inode号的关系 ---------------- ...

  3. PopUpWindow使用详解(二)——进阶及答疑

    相关文章: 1.<PopUpWindow使用详解(一)--基本使用> 2.<PopUpWindow使用详解(二)--进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

  4. Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  5. linux 进程间通信 dbus-glib【实例】详解二(下) 消息和消息总线(ListActivatableNames和服务器的自动启动)(附代码)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  6. linux 进程间通信 dbus-glib【实例】详解二(上) 消息和消息总线(附代码)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  7. Android Gradle 自定义Task详解二:进阶

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78523958 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  8. Android Loader 异步加载详解二:探寻Loader内部机制

    Android Loader 异步加载详解二:探寻Loader内部机制 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/7025991 ...

  9. EXT核心API详解(二)-Array/Date/Function/Number/String

    EXT核心API详解(二)-Array/Date/Function/Number/String Array类 indexOf( Object o )  Number object是否在数组中,找不到返 ...

  10. OS--进程间通信详解(二)

    OS–进程间通信详解(二) 文章目录 OS--进程间通信详解(二) 一.进程间通信 1.互斥量 Futexes Pthreads中的互斥量 2.管程 3.消息传递 消息传递系统的设计要点 用消息传递解 ...

最新文章

  1. 如何保证MySQL和Redis的数据一致性?10张图带你搞定!
  2. 生态伙伴 | Tita入驻飞书啦,从目标制定到项目执行,帮你一站式解决!
  3. POJ - 1966 Cable TV Network(最小割-最大流)
  4. base cap 分布式_高并发架构系列:详解分布式一致性ACID、CAP、BASE,以及区别
  5. 思科路由器的硬件组成
  6. Windows下Visual studio 2013 编译 Audacity
  7. 漫画:如何用栈实现队列
  8. 怎么挪动_2020蚕茧多少钱一斤,蚕茧怎么做成蚕丝被
  9. 知乎高赞:一行代码凭什么躺普通程序员的10年工资?
  10. 亚马逊 html格式转换,亚马逊七种产品描述常用HTML代码,文字颜色代码让Listing更出彩...
  11. 【Clickhosue】MySQL 没有主键导致CK不可用 The db.scene cannot be materialized, because there is no primary keys
  12. Android中ListView分页加载数据
  13. 优雅使用百度云进行表格OCR识别(python版)
  14. c语言双人贪吃蛇小游戏代码,关于C 双人贪吃蛇显示的问题
  15. 计算机操作系统复习题
  16. java生产者消费者模型
  17. 人口流向数据_中国人口流动数据挖掘分析云平台
  18. STM32物联网项目-HMI串口屏
  19. 自动代理老是自动打开上不了网的解决办法
  20. 解决页面间体验问题的纯前端容器Lath

热门文章

  1. 检查方程的计算机,计算器解方程
  2. 广东省2009届毕业生专场招聘会
  3. WPS怎么打印到一张纸上?
  4. mysql错误码:2006-MySQL server has gone away详解
  5. 写一个函数,输入一个4位数字,要求输出这四个数字字符,但每两个数字间空两个空格。 如输入1990,应输出“1 9 9 0”
  6. [Pandas] 缺失值填充 df.fillna()
  7. BTC/BSV真假中本聪之郁金香信托
  8. C# 倒计时数秒工具
  9. 证书链不完整及解决方法
  10. javascript——数组、数组遍历、forEach、增加删除元素