requirejs(一)
RequireJs已经流行很久了,我们在项目中也打算使用它。它提供了以下功能:
- 声明不同js文件之间的依赖
- 可以按需、并行、延时载入js库
- 可以让我们的代码以模块化的方式组织
初看起来并不复杂。
在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 中通常用来做两件事:
- 配置requirejs 比如项目中用到哪些模块,文件路径是什么
- 载入程序主模块
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一共提供了两个全局变量:
- requirejs/require: 用来配置requirejs及载入入口模块。如果其中一个命名被其它库使用了,我们可以用另一个
- 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(一)的更多相关文章
- bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...
- 实现一个类 RequireJS 的模块加载器 (二)
2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...
- 使用RequireJS并实现一个自己的模块加载器 (一)
RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...
- 使用gulp解决RequireJS项目前端缓存问题(二)
1.前言 这一节,我们主要解决在上一节<使用gulp解决RequireJSs项目前端缓存问题(一)>末尾提到的几个问题: 对通过require-config.js引入的js文件修改后,没有 ...
- AngularJs2与AMD加载器(dojo requirejs)集成
现在是西太平洋时间凌晨,这个问题我鼓捣了一天,都没时间学英语了,英语太差,相信第二天我也看不懂了,直接看结果就行. 核心原理就是require在AngularJs2编译过程中是关键字,而在浏览器里面运 ...
- angularjs集成requirejs
其实说成使用requirejs加载angularjs应用会更贴切一些 <body> <span ng-controller="homeController"> ...
- 使用gulp解决RequireJS项目前端缓存问题(一)
1.前言 前端缓存一直是个令人头疼的问题,你有可能见过下面博客园首页的资源文件链接: 有没有发现文件名后面有一串不规则的东东,没错,这就是运用缓存机制,我们今天研究的就是这种东西. 先堵为快,猛戳链接 ...
- CommonJS, AMD 和 RequireJS之间的关系(转载)
先说说CommonJS CommonJS - 大家是不是觉得JavaScript仅仅是一个客户端的编译语言,其实JavaScript设计之初不仅仅是针对客户端设计的语言.后来只是由于Web的迅速流行, ...
- RequireJS与SeaJS模块化加载示例
web应用越变的庞大,模块化越显得重要,尤其Nodejs的流行,Javascript不限用于浏览器,还用于后台或其他场景时,没有Class,没有 Package的Javascript语言变得难以管理, ...
- 【requireJS源码学习01】了解整个requireJS的结构
前言 现在工作中基本离不开requireJS这种模块管理工具了,之前一直在用,但是对其原理不甚熟悉,整两天我们来试着学习其源码,而后在探寻其背后的AMD思想吧 于是今天的目标是熟悉requireJS整 ...
随机推荐
- 常见input输入框 点击 发光白色外阴影 focus
先看看具体实现的效果 第一就是点击input 实现的效果 默认谷歌点击input是蓝色边框 去掉用outline:0; 实现效果用focus 默认状态的边框颜色一般较重 如border:1px s ...
- java 获取文件列表,并按照文件名称排序
需求:获取全部的日志文件,并按照文件名称倒序排列,把最新的文件放在最前1.获取全部的日志文件:(方法:public List<String> ergodic(File file,List& ...
- 1、Jsp页面
一.JSP(java server page):是以Java语言为基础的动态网页生成技术. 1.特点: a).以 .jsp 为后缀的文本文件,不需要编译(相对于程序猿来说不需要编译) b).以html ...
- PHP正则表达式详解(一)
前言: 半年前我对正则表达式产生了兴趣,在网上查找过不少资料,看过不少的教程,最后在使用一个正则表达式工具RegexBuddy时,发现他的教程写的非常好,可以说是我目前见过最好的正则表达式教程.于是一 ...
- Linux C fcntl()函数详解
fcntl系统调用 功能描述:根据文件描述词来操作文件的特性. 用法: int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg ...
- 修改Linux的SSH远程连接端口 技巧
将SSH终端服务的端口由 22 修改为别的端口以防攻击黑客直接猜解您的服务器密码 首先修改配置文件 vi /etc/ssh/sshd_config 找到 #Port 22 一段,这里是标识默认使用 2 ...
- HDU 4946 Area of Mushroom(构造凸包)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4946 题目大意:在一个平面上有n个点p1,p2,p3,p4....pn,每个点可以以v的速度在平面上移 ...
- PYTHON 写函数,检查用户传入的对象(字符串、列表、元组)的每一个元素是否含有空内容。
def shifou_space(args): ret = True for a in args: if a.isspace(): ret = False break return ret resul ...
- 如何查找本地的ip
输入cmd调出指令框然后输入ipconfig指令用快捷键ctrl右侧的键+R
- centos 安装 rabbitMQ
此类文章一大堆,本文主要站在开发角度保证基本rabbitmq的基本访问. 系统:centos6 64bit 官方指引:https://www.rabbitmq.com/install-rpm.html ...