requireJS 可以很輕易的將一個專案中的JavaScript程式碼分割成若干個模組(module)。並且requireJS推薦一個模組就是一個文件,所以,你將獲得一些零碎的具有相互依賴關係的JS文件。模組化的好處也淺顯意見,那就是大幅增強程式碼的可讀性、易維護性、可擴展性、減少全局污染等。
目錄:
基本概念
#requireJS的歷史發展
##模組化的優點
require 實戰 引入requireJS
參數設定
載入設定檔
訂模組# ## 所依賴的函數式定義
載入模組
模組的回值定義
return常用參數
urlArgs
# scriptType
waitSeconds
deps
map
packages
rquire 壓縮
##它問題
# 1 . timeout逾時問題
2. 循環依賴問題
3. CDN回退
4.定義AMD插件
5. 關於require的預定義模組# .本地文件的問題 7. 關於R.js - shim功能的說明 8.因為自身設計的問題
因為自身設計的問題
##因為自身設計的問題
##因為自身設計的問題。不足,JavaScript 這門語言其實並沒有模組化這種概念與機制,所以想實現如JAVA,PHP等一些後台語言的模組化開發,那麼我們必須藉助requireJS 這個前端模擬模組化的插件,雖然我們不需要去了解它的實現原理,但是大致去了解它是如何運作的,我相信這會讓我們更容易上手。
requireJS使用head.appendChild()将每一个依赖加载为一个script标签。 requireJS等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。
requireJS的歷史發展
在說JavaScript模組化之前,我們必須提CommonJS(原名叫ServerJs),這個社群可謂大牛雲集,他們為NodeJS制定過模組化規範Modules/1.0 ,並得到了廣泛的支持。在為JavaScript客製化模組化規範時,討論的都是在Modules/1.0 上進行改進,但是Modules/1.0 是專門為服務端制定的規範,所以要想套用在客服端環境的JS上,情況就會有很大的不同,例如,對於服務端加載一個JS文件,其耗費的時間幾乎都是可以忽略不計的,因為這些都是基於本地環境,而在客戶端瀏覽器上加載一個文件,都會發送一個HTTP請求,並且還可能存在跨域的情況,也就是說資源的加載,到執行,是會存在一定的時間消耗與延遲。
所以社群的成員們意識到,要想在瀏覽器環境中也能模組化開發,則需要對現有規範進行更改,而就在社群討論制定規範的時候內部發生了比較大的分歧,分裂出了三個主張,漸漸的形成三個不同的派別:
1.Modules/1.x派 这一波人认为,在现有基础上进行改进即可满足浏览器端的需要,既然浏览器端需要function包装,需要异步加载,那么新增一个方案,能把现有模块转化为适合浏览器端的就行了,有点像“保皇派”。基于这个主张,制定了Modules/Transport(http://wiki.commonjs.org/wiki/Modules/Transport)规范,提出了先通过工具把现有模块转化为复合浏览器上使用的模块,然后再使用的方案。 browserify就是这样一个工具,可以把nodejs的模块编译成浏览器可用的模块。(Modules/Transport规范晦涩难懂,我也不确定browserify跟它是何关联,有知道的朋友可以讲一下) 目前的最新版是Modules/1.1.1(http://wiki.commonjs.org/wiki/Modules/1.1.1),增加了一些require的属性,以及模块内增加module变量来描述模块信息,变动不大。 2. Modules/Async派 这一波人有点像“革新派”,他们认为浏览器与服务器环境差别太大,不能沿用旧的模块标准。既然浏览器必须异步加载代码,那么模块在定义的时候就必须指明所依赖的模块,然后把本模块的代码写在回调函数里。模块的加载也是通过下载-回调这样的过程来进行,这个思想就是AMD的基础,由于“革新派”与“保皇派”的思想无法达成一致,最终从CommonJs中分裂了出去,独立制定了浏览器端的js模块化规范AMD(Asynchronous Module Definition)(https://github.com/amdjs/amdjs-api/wiki/AMD) 3. Modules/2.0派 这一波人有点像“中间派”,既不想丢掉旧的规范,也不想像AMD那样推到重来。他们认为,Modules/1.0固然不适合浏览器,但它里面的一些理念还是很好的,(如通过require来声明依赖),新的规范应该兼容这些,AMD规范也有它好的地方(例如模块的预先加载以及通过return可以暴漏任意类型的数据,而不是像commonjs那样exports只能为object),也应采纳。最终他们制定了一个Modules/Wrappings(http://wiki.commonjs.org/wiki/Modules/Wrappings)规范,此规范指出了一个模块应该如何“包装”,包含以下内容:
實際上這三個流派誰都沒有勝過誰,反而是最後的AMD,CMD 規範紮根在這在三個流派之上,吸取它們所提出的優點不斷壯大。
總的來說AMD,CMD都是從commonJS規範中結合瀏覽器現實情況,並且吸收三大流派的優點而誕生。其中CMD是國內大牛所製定的規範,其實現的工具是seaJS,而AMD則是國外大牛所製定的,其實現技術則是requireJS
模組化的優點
#既然我們已經詳細的了解了「前端模組化」的歷史與發展,那麼我們也要大致了解模組開發的好處,畢竟這是我們學習的動力。
1. 作用域污染 小明定义了 var name = 'xiaoming'; N ~ 天之后: 小王又定义了一个 var name = 'xiaowang'; 2. 防止代码暴漏可被修改: 为了解决全局变量的污染,早期的前端的先驱们则是以对象封装的方式来写JS代码: var utils = { 'version':'1.3' }; 然而这种方式不可以避免的是对象中的属性可被直接修改:utils.version = 2.0 。 3. 维护成本的提升。 如果代码毫无模块化可言,那么小明今天写的代码,若干天再让小明自己去看,恐怕也无从下手。 4. 复用与效率 模块与非模块的目的就是为了复用,提高效率
總的來說,前端的模組化就是在眼瞎與手殘的過程進行發展的,大致我們可以總結一下幾時代:
無序(洪荒時代) :自由的書寫代碼。
函數時代 :將程式碼關入了籠子之中。
物件導向的方式。
匿名自執行函數:其典型的代表作就是JQ。
偽模組開發(CMD/AMD)
#模組化開發(尚未誕生的ES6標準)
我们相信未来必将更加光明,但是回顾现在,特别是在国内的市场环境中IE浏览器依然占据半壁江山,所以基于ES6的模块特性依然任重道远,因此,在光明还未播撒的时刻,就让我们率先点燃一朵火苗照亮自己,而这朵火苗就是 ———— requireJS
require 实战
下面我将化整为零的去讲解requireJS在一个项目的具体使用方式以及需要注意的事项。
引入requireJS
通过 <script></script>
标签,将require.js 文件引入到当前的 HTML 页面中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>RequireJS 实战</title> </head> <body> <script src="js/require.js"></script> </body> </html>
参数配置
requireJS 常用的方法与命令也就两个,因此requireJS使用起来非常简单。
require
define
其中define
是用于定义模块,而require
是用于载入模块以及载入配置文件。
在requireJS中一个文件就是一个模块,并且文件名就是该模块的ID,其表现则是以key/value的键值对格式,key即模块的名称(模块ID),而value则是文件(模块)的地址,因此多个模块便有多个键值对值,这些键值对再加上一些常用的参数,便是require的配置参数,这些配置参数我们通常会单独保存在一个JS文件中,方便以后修改、调用,所以这个文件我们也称之为“配置文件”。
下面是requireJS的基本参数配置:
//index.html <script> require.config({ baseUrl:'js/', paths:{ 'jquery':'http://xxxx.xxx.com/js/jquery.min', 'index':'index' } }); require(['index']); </script>
require.config()
是用于配置参数的核心方法,它接收一个有固定格式与属性的对象作为参数,这个对象便是我们的配置对象。
在配置对象中 baseUrl
定义了基准目录,它会与 paths
中模块的地址自动进行拼接,构成该模块的实际地址,并且当配置参数是通过script
标签嵌入到html文件中时,baseUrl默认的指向路径就是该html文件所处的地址。paths
属性的值也是一个对象,该对象保存的就是模块key/value值。其中key便是模块的名称与ID,一般使用文件名来命名,而value则是模块的地址,在requireJS中,当模块是一个JS文件时,是可以省略 .js 的扩展名,比如 “index.js” 就可以直接写成 “index” 而当定义的模块不需要与 baseUrl
的值进行拼接时,可以通过 "/"
与 http://
以及 .js
的形式来绕过 baseUrl
的设定。
示例:
require.config({ baseUrl:'js/', paths:{ 'jquery':'http://xxx.xxxx.com/js/jquery.min', 'index':'index' } }); require(['index']);
实际上,除了可以在require.js加载完毕后,通过require.config()
方法去配置参数,我们也可以在require.js加载之前,定义一个全局的对象变量 require 来事先定义配置参数。然后在require.js被浏览器加载完毕后,便会自动继承之前配置的参数。
<script> var require = { baseUrl: 'js/', paths: { 'jquery': 'http://xxx.xxxx.com/js/jquery.min', 'index': 'index' }, deps:[index] }; </script> <script src="js/require.js"></script>
不论是在require.js加载之前定义配置参数,还是之后来定义,这都是看看我们需求而言的,这里我们举例的配置参数都是放入到script标签中,然后嵌入到HTML页面的内嵌方式,在实际使用时,我们更多的则是将该段配置提取出来单独保存在一个文件中,并将其取名为 app.js
,而这个 app.js
便是我们后面常说到的配置文件。
另外还有一个“接口文件”的概念,requireJS中,所谓接口文件指的便是require.js加载完毕后第一个加载的模块文件。
加载配置文件
现在我们知道require的配置有两种加载方式,一种是放入到script标签嵌入到html文件中,另一种则是作为配置文件 app.js
来独立的引入。
独立的引入配置文件也有两种方式,一种是通过script标签加载外部JS文件形式:
<script src="js/require.js"></script><script src="js/app.js"></script>
另一种方式则是使用 require 提供的 data-main
属性,该属性是直接写在引入require.js的script标签上,在require.js 加载完毕时,会自动去加载配置文件 app.js。
html<script data-main="js/app" src="js/require.js"></script>
通过 data-main
去加载入口文件,便会使配置对象中的 baseUrl
属性默认指向地址改为 app.js 所在的位置,相比之下我更加推荐这种方式,因为它更可能的方便快捷。
当我们的项目足够的庞大时,我也会推荐将入口文件作为一个普通的模块,然后在这个模块中,根据业务的不同再去加载不同的配置文件。
//define.js define(['app1','app2','app3','app4'],function(app1,app2,app3,app4){ if(page == 'app1'){ require.config(app1); }else if(page == 'app2'){ require.config(app2); }else if(page == 'app3'){ require.config(app3); }else{ require.config(app4); } })
当然关于模块的定义和载入我们后面会详细的讲解到,这里只需要有一个概念即可。
定义模块
在我们选择requireJS来模块化开发我们的项目或者页面时,就要明确的知道我们以后所编写的代码或者是某段功能,都是要放在一个个定义好的模块中。
下面是requireJS定义模块的方法格式:
define([id,deps,] callback);
ID:模块的ID,默认的便是文件名,一般无需使用者自己手动指定。
deps:当前模块所以依赖的模块数组,数组的每个数组元素便是模块名或者叫模块ID。
callback:模块的回调方法,用于保存模块具体的功能与代码,而这个回调函数又接收一个或者多个参数,这些参数会与模块数组的每个数组元素一一对应,即每个参数保存的是对应模块返回值。
根据 define()
使用时参数数量的不同,可以定义以下几种模块类型:
简单的值对
当所要定义的模块没有任何依赖也不具有任何的功能,只是单纯的返回一组键值对形式的数据时,便可以直接将要返回的数据对象写在 define
方法中:
define({ 'color':'red', 'size':'13px', 'width':'100px' });
这种只为保存数据的模块,我们称之为“值对”模块,实际上值对模块不仅可以用于保存数据,还可以保存我们的配置参数,然后在不同的业务场景下去加载不同的配置参数文件。
示例:
//app1.js define({ baseUrl:'music/js/', paths:{ msuic:'music', play:'play' } });
//app2.js define({ baseUrl:'video/js/', paths:{ video:'video', play:'play' } });
非依赖的函数式定义
如果一个模块没有任何的依赖,只是单纯的执行一些操作,那么便可以直接将函数写在 define
方法中:
define(function(require,exports,modules){ // do something return { 'color':'red', 'size':'13px' } });
依赖的函数式定义
这种带有依赖的函数式模块定义,也是我们平时常用到的,这里我们就结合实例,通过上面所举的 index
模块为例:
//index.js define(['jquery','./utils'], function($) { $(function() { alert($); }); });
从上面的示例中我们可以看出 index
模块中,依赖了 'jquery' 模块,并且在模块的回调函数中,通过 $
形参来接收 jquery
模块返回的值,除了 jquery
模块,index模块还依赖了 utils
模块,因为该模块没有在配置文件中定义,所以这里以附加路径的形式单独引入进来的。
载入模块
在说载入模块之前,我们先聊聊“模块依赖”。模块与模块之间存在着相互依赖的关系,因此就决定了不同的加载顺序,比如模块A中使用到的一个函数是定义在模块B中的,我们就可以说模块A依赖模块B,同时也说明了在载入模块时,其顺序也是先模块A,再模块B。
在require中,我们可以通过 require()
方法去载入模块。其使用格式如下:
require(deps[,callback]);
deps:所要载入的模块数组。
callback:模块载入后执行的回调方法。
这里就让我们依然使用上述的 index 模块为例来说明
示例:
require.config({ paths:{ 'index':'index' } }); require(['index']);
requireJS 通过 require([])
方法去载入模块,并执行模块中的回调函数,其值是一个数组,数组中的元素便是要载入的模块名称也就是模块ID,这里我们通过 require(['index'])
方法载入了 index 这个模块,又因为该模块依赖了 jquery 模块,所以接着便会继续载入jquery模块,当jquery模块加载完成后,便会将自身的方法传递给形参 $
最后执行模块的回调方法,alert出$参数具体内容。
这里我们可以小小的总结一下,实现模块的载入除了 require([],fn)
的主动载入方法,通过依赖也可以间接载入对应的模块,但是相比较而言require方式载入模块在使用上更加灵活,它不仅可以只载入模块不执行回调,也可以载入模块然后执行回调,还可以在所定义的模块中,按需载入所需要用到的模块,并且将模块返回的对象或方法中保存在一个变量中,以供使用。
这种按需载入模块,也叫就近依赖模式,它的使用要遵循一定的使用场景:
当模块是非依赖的函数式时,可以直接使用
define(function(require,exports,modules){ var utils = require('utils'); utils.sayHellow('hellow World') })
当模块是具有依赖的函数式时,只能够以回调的形式处理。
define(['jquery'], function($) { $(function() { require(['utils'],function(utils){ utils.sayHellow('Hellow World!'); }); }); });
当然聪明伶俐的你,一定会想到这样更好的办法:
define(['jquery','require','exports','modules'], function($,require,exports,modules) { $(function() { //方式一 require(['utils'],function(utils){ utils.sayHellow('Hellow World!'); }); //方式二: var utils = require('utils'); utils.sayHellow('hellow World') }); });
模块的返回值
require中定义的模块不仅可以返回一个对象作为结果,还可以返回一个函数作为结果。实现模块的返回值主要有两种方法:
return 方式
// utils.js define(function(require,exports,modules){ function sayHellow(params){ alert(params); } return sayHellow }); // index.js define(function(require,exports,modules){ var sayHellow = require('utils'); sayHellow('hellow World'); })
如果通过return 返回多种结果的情况下:
// utils.js define(function(require,exports,modules){ function sayHellow(params){ alert(params); } function sayBye(){ alert('bye-bye!'); } return { 'sayHellow':sayHellow, 'sayBye':sayBye } }); // index.js define(function(require,exports,modules){ var utils = require('utils'); utils.sayHellow('hellow World'); })
exports导出
// utils.js define(function(require,exports,modules){ function sayHellow(params){ alert(params); } exports.sayHellow = sayHellow; }) // index.js define(function(require,exports,modules){ var utils = require('utils'); utils.sayHellow('hellow World'); });
这里有一个注意的地方,那就是非依赖性的模块,可以直接在模块的回调函数中,加入以下三个参数:
require:加载模块时使用。
exports:导出模块的返回值。
modules:定义模块的相关信息以及参数。
非标准模块定义
在 require.config()
方法的配置对象中有一个 shim
属性,它的值是一个对象,可以用于声明非标准模块的依赖和返回值。
所谓的 “非标准模块” 指的是那些不符合的AMD规范的JS插件。
下面我们先看看基本的 shim
配置参数:
require.config({ baseUrl:'js/', paths:{ 'jquery':'http://xxx.xxxx.com/js/jquery.min', 'index':'index', 'say':'say', 'bar':'bar', 'tools':'tools' }, shim:{ 'tools':{ deps:['bar'], exports:'tool' }, 'say':{ deps:['./a','./b'], init:function(){ return { 'sayBye':sayBye, 'sayHellow':sayHellow } } } } }); require(['index']);
这里需要注意的是如果所加载的模块文件是符合AMD规范,比如通过 define
进行定义的,那么require默认的优先级将是标准的,只有在不符合标准的情况下才会采用shim中定义的参数。
在 index 模块执行时:
define(['jquery','tool','say'],function($,tool,say){ tool.drag(); say.sayHellow(); say.sayBye(); })
上面的示例中,关于 shim
中有三个重要的属性,它们分别是:
deps: 用于声明当前非标准模块所依赖的其它模块,值是一个数组,数组元素是模块的名称或者是ID。
exports:用于定义非标准模块的全局变量或者方法。值一般是一个字符串。
init:用于初始,处理,非标准模块的全局变量或者是方法,常用于当非标准模块存在多个全局变量以及方法,值是一个函数。
常用参数
在 require.config
中还存在其他的常用属性设置。
urlArgs
RequireJS获取资源时附加在URL后面的额外的query参数。作为浏览器或服务器未正确配置时的“cache bust”手段很有用。使用cache bust配置的一个示例:javascript:;urlArgs: "bust=" + (new Date()).getTime()
在开发中这很有用,但请记得在部署到生成环境之前移除它。
scriptType
指定RequireJS将script标签插入document时所用的type=""值。默认为“text/javascript”。想要启用Firefox的JavaScript 1.8特性,可使用值“text/javascript;version=1.8”。
waitSeconds
通过该参数可以设置requireJS在加载脚本时的超时时间,它的默认值是7,即如果一个脚本文件加载时长超过7秒钟,便会放弃等待该脚本文件,从而报出timeout超时的错误信息,考虑到国内网络环境不稳定的因素,所以这里我建议设置为0。当然一般不需要去改动它,除非到了你需要的时候。
deps
用于声明require.js在加载完成时便会自动加载的模块,值是一个数组,数组元素便是模块名。
callback
当deps中的自动加载模块加载完毕时,触发的回调函数。
config
config属性可以为模块配置额外的参数设定,其使用格式就是以模块名或者模块ID为key,然后具体的参数为value。
//app.js require.config({ baseUrl:'js/', paths:{ 'jquery':'http://xx.xxxx.com/js/jquery.min', 'index':'index' }, config:{ 'index':{ 'size':13, 'color':'red' } } }); //index.js define(['jquery','module'],function($,module){ console.log(module.config().size) });
这里要引起我们注意的地方就是依赖的'module'模块,它是一个预先定义好的值,引入该值,在当前模块下便可以调用module对象,从该对象中执行 config()
方法便可以生成改模块的参数对象。
map
[略],暂时还未弄明白其具体使用方式,后续会继续保持关注,如果你知晓其作用,麻烦你一定要与我联系。
packages
[略],暂时还未弄明白其具体使用方式,后续会继续保持关注,如果你知晓其作用,麻烦你一定要与我联系。
rquire 压缩
RequireJS 会将完整项目的JavaScript代码轻易的分割成苦干个模块(module),这样,你将获得一些具有互相依赖关系的JavaScript文件。在开发环境中,这种方式可以让我们的代码更具有模块化与易维护性。但是,在生产环境中将所有的JavaScript文件分离,这是一个不好的做法。这会导致很多次请求(requests),即使这些文件都很小,也会浪费很多时间。因此我们可以通过合并这些脚本文件压缩文件的大小,以减少请求的次数与资源的体积来达到节省加载时间的目的,所以这里我们就要提到一个关于requireJS 延伸,那就是 r.js。
r.js 是一个独立的项目,它作用在nodeJS环境中,可以实现对JS代码的合并压缩。
使用r.js 要具有以下几个条件:
r.js 源文件
bulid.js (即属于r.js的配置文件)
nodeJS 环境
r.js 可以直接丢在项目的根目录上,build.js 是 r.js 的配置文件,由开发者自己新建,与r.js同目录。其一般的目录结构如下:
[project] /js /css /images index.html r.js build.js
r.js 下载
nodeJS环境,以及r.js都好办,重要的就是掌握配置文件的使用 -- build.js,下面我们就详细的说说它。
({ //(选填)app的顶级目录。如果指定该参数,说明您的所有文件都在这个目录下面(包括baseUrl和dir都以这个为根目录)。如果不指定,则以baseUrl参数为准 appDir: './', // 输出目录。如果不指定,默认会创建一个build目录 dir: 'pack', // 模块所在默认相对目录,如果appDir有指定,则baseUrl相对于appDir。 baseUrl: 'js/', paths: { 'index': 'index', 'a': 'a', 'b': 'b', 'c': 'c' }, //过滤规则,匹配到的文件将不会被输出到输出目录去 fileExclusionRegExp: /^(r|build)\.js|.*\.scss$/, /* JS 文件优化方式,目前支持以下几种: uglify: (默认) 使用 UglifyJS 来压缩代码 closure: 使用 Google's Closure Compiler 的简单优化模式 closure.keepLines: 使用 closure,但保持换行 none: 不压缩代码 */ optimize: 'none', /* 允许优化CSS,参数值: “standard”: @import引入并删除注释,删除空格和换行。删除换行在IE可能会出问题,取决于CSS的类型 “standard.keepLines”: 和”standard”一样但是会保持换行 “none”: 跳过CSS优化 “standard.keepComments”: 保持注释,但是去掉换行(r.js 1.0.8+) “standard.keepComments.keepLines”: 保持注释和换行(r.js 1.0.8+) “standard.keepWhitespace”: 和”standard”一样但保持空格 */ optimizeCss: '“standard”', // 是否忽略 CSS 资源文件中的 @import 指令 cssImportIgnore: null, //参与压缩的主模块,默认情况下会将paths模块中定义的模块都压缩合并到改模块中,通过exclude 可以排除参与压缩的模块,其中模块的地址都是相对于baseUrl的地址。 modules: [{ name: 'index', exclude: ['c'] }], // 包裹模块 wrap: true, // 自定义包裹模块,顾名思义就是使用特定内容去包裹modules指定的合并模块内容,如此一来 define/require 就不再是全局变量,在 end 中可以暴露一些全局变量供整个函数使用 wrap: { start: "(function() {", end: "}(window));" }, removeCombined: false, //如果shim配置在requirejs运行过程中被使用的话,需要在这里重复声明,这样才能将依赖模块正确引入。 shim: {} // 载入requireJS 的配置文件,从而使用其中的paths 以及 shim 属性值。通过指定该属性,可以省去我们在bulid.js中重复定义 paths 与 shim属性。 mainConfigFile:"js/app.js", })
以上环节都准备好了之后,就可以在终端中允许打包压缩命令: node r.js -o build.js
。
当执行该命令后,r.js
会将自身所在目录的所有资源连同目录重新拷贝一份到输出目录(dir)中。然后再输出目录进行最后的合并与压缩操作。
其它问题
timeout超时问题
该问题一般是 waitSeconds
属性值导致,解决的方法有两个,一个是将 waitSeconds
的值设置更长时间,比如17s,另一个就是将其值设置为0,让其永不超时。
循环依赖问题
何为循环依赖?
如果存在两个模块,moduleA 与 moduleB, 如果 moduleA 依赖 moduleB ,moduleB也依赖了moduleA,并且这中情况下,便是循环依赖。
循环依赖导致的问题!
如果两个模块循环依赖,并且A中有调用B中的方法,而B中也有调用A中的方法,那么此时,A调用B正常,但是B中调用A方法,则会返回 undefined
异常。
如何解决循环依赖的问题?
通过 require([],fn)
解决
此时在模块B中,我们通过引入 require 依赖,然后再通过 require()
方法去载入模块A,并在回调中去执行。
define(['require','jquery'],function(require,$){ function bFunction(){ alert('this is b module'); } require(['moduleA'],function(m){ m() // 执行传递过来方法 }); return bFunction; });
这里要引起我们注意的地方就是依赖的'module'模块,它是一个预先定义好的值,引入该值,在当前模块下便可以调用 require
方法。
通过 exports
解决
define(['exports','jquery'],function(exports,$){ function bFunction(){ exports.aFunction(); alert('this is b module'); } exports.bFunction = bFunction; });
相同的这里依赖的 module
模块也是一个预先定义好的值,,引入该值,在当前模块下便可以调用 exports
对象设定当前模块的返回值。
而通过 exports
所解决的循环依赖问题,有一个需要注意的地方,那就是方法的执行必须要放入到当前定义方法的回调中,因为我们不能确定 moduleA 与 moduleB的加载顺序。
CDN回退
如果我们不确定一个模块的加载正确,我们可以在 require.config()
方法中将模块的地址替换为一个数组,数组的元素,便是同一模块的多个地址。
requirejs.config({ paths: { jquery: [ '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js', 'lib/jquery' ] } });
定义AMD插件
有时候我们自己编写的一款插件,我们需要它能够在任何环境中都能起作用,比如在引入requireJS的AMD环境下可以作为符合AMD规范的插件,进行模块式加载调用,而在普通的浏览器环境中也可以正常的执行。
想实现这一功能,其实很简单,只需要按照下例格式去编写插件即可。
// say.js 基于JQ扩展的插件。 (function(win, factory) { if ('function' === typeof define && define.amd) { define(['jquery'], function($) { return new factory(win, $) }); } else { factory(win, $); } }(window, function(win, $) { var say = function(value) { alert(value); } if ('function' === typeof define && define.amd) { return say; } else if ($ && 'function' === typeof $) { $.say = function(v) { return new say(v); } } else { win.say = function(v) { return new say(v); } } })); // index.js define(['say'],function(say){ say('hellow requireJS'); })
关于require的预定义模块
关于这个问题,我们上面也有说到,这里就进行一次总结。
我们可以这样理解,对于 requireJS 来说,除了我们自己使用require.config()
定义的模块,它内部也有自己预先定义好的模块,比如:require
,exports
,modules
,在使用时,我们无需在 require.config()
去中定义,而是可以直接在依赖中引入使用,比如:
//index.js define(['jquery','config','require','exports','module'],function($,config,require,exports,module){ $(function(){ require.config(config); // 载入配置文件 exports.data = 'index Module Return Value' //定义模块的返回值。 modules.config().color; // 接受在配置文件中为该模块配置的参数数据。 }) });
关于R.js压缩非本地文件的问题
在 r.js
中是无法合并压缩远程文件的,它只能操作本地文件,因此这就带来一个问题,当我们进行模块的压缩合并时,若某个模块存在着对远程模块(文件)的依赖时,使用 r.js
进行操作便会报错,虽然可以将这个远程文件拷贝到本地来解决这一问题,但是如果像一些公用的资源例如JQ插件等,如果让每个项目都在本地放入一个 common
资源包,这就脱离了我们的实际意义。
({ paths:{ jquery:'http://xxx.com/js/jquery.min' } })
此时进行打包的时候在就会报错。但是如果我们不在 paths
中去声明 jquery
模块,当打包的时候,r.js
发现其它模块有依赖 jquery
的,但是你又没有在 build.js
中声明,依然会报错阻碍运行。
那么有没有一个好的办法呢?比如虽然声明了 jquery
模块,但是值却不是远程的文件,本地也不存在该文件,更不会报错。答案是有的,那就是对(不需要参与压缩合并的)远程的资源模块设置值为 empty:。 "javascript:; ({ paths:{ jquery:'empty:' } }) " 或者是在执行命令时,指定参数值为空:
node r.js -o build.js paths.jquery=empty:`
关于R.js - shim功能的说明
R.js 用于合并多个模块(多个文件),以及压缩文件中的JS代码,也就是说在这个合并后的文件中会包含多个 define
定义的模块,而这个合并后的文件也就是这个页面的入口文件,并且rquire的config配置也会在其中。
模块的合并,对于R.js来言,它会以 build.js
中 paths
属性定义的模块为参考,然后到要合并的模块只能中去匹配依赖,匹配到了就合并到当前文件中。如果参与合并的所有模块有某些依赖顺序上的调整,则可以通过 shim
属性去调整合并时的前后顺序。
//build.js ({ 'paths':{ 'a':'a', 'b':'b' }, 'shim':{ 'a':{ 'deps':['b'] } }, wrapShim:true })
此时合并到主文件后,b
模块的内容就会在 a
模块之前。
define('b',[],function(){}), define('a',[],function(){})
最后强调一点,对于通过 exclude
属性排除合并的模块,使用 shim
并不会产生作用,因为它只对合并在一个文件中的模块有效。
关于require加载CSS的问题
requireJS不仅仅只加载JS文件,实际上它还可以加载CSS样式文件,但是这需要借助一个requireJS插件才能实现。
下载地址:require-css.js
使用上,有两种方式,一种在配置参数中进行声明:
var require = { baseUrl:'js/', paths:{ 'index':'index', 'a':'a' }, shim:{ 'a':{ deps:['css!../css/a.css'] } }, deps:['index'] };
//index.js define(['a']); // 载入模块不执行任何操作。
另一种是直接在模块中进行依赖声明
define(['css!../css/a.css']);
最后说下对 css!../css/index.css
的理解吧,首先 !
是插件与插件参数的分割符号,因此"css"就是插件的模块名,requireJS会先去检查 css
这个模块是否有在配置文件中声明,如果没有则会默认在 baseUrl
指向的路径下去载入,而分隔符右边的 '../css/a.css' 就是插件要使用的参数,这里便是要载入的css文件地址。
以上是一份超詳細的requireJS介紹及應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

禪工作室 13.0.1
強大的PHP整合開發環境

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Dreamweaver Mac版
視覺化網頁開發工具