使用 RequireJS 优化 Web 应用前端
基于 AMD(Asynchronous Module Definition)的 JavaScript 設(shè)計(jì)已經(jīng)在目前較為流行的前端框架中大行其道,jQuery、Dojo、MooTools、EmbedJS 等紛紛在其最新版本中加入了對(duì) AMD 的支持。本文介紹的是另一款較為精簡(jiǎn)的 RequireJS 框架,既想使用 AMD 的特性又不想引入一個(gè)龐大的庫(kù)的開(kāi)發(fā)人員,不妨試試 RequireJS。RequireJS 可以幫助用戶(hù)異步按需的加載 JavaScript 代碼,并解決 JavaScript 模塊間的依賴(lài)關(guān)系,提升了前端代碼的整體質(zhì)量和性能。
?
AMD 簡(jiǎn)介
前端開(kāi)發(fā)在近一兩年發(fā)展的非常快,JavaScript 作為主流的開(kāi)發(fā)語(yǔ)言得到了前所未有的熱捧。大量的前端框架出現(xiàn)了,這些框架都在嘗試著解決一些前端開(kāi)發(fā)中的共性問(wèn)題,但是實(shí)現(xiàn)又不盡相同。在這個(gè)背景下,CommonJS 社區(qū)誕生了,為了讓前端框架發(fā)展的更加成熟,CommonJS 鼓勵(lì)開(kāi)發(fā)人員一起在社區(qū)里為一些完成特定功能的框架制定規(guī)范。AMD(Asynchronous Module Definition)就是其中的一個(gè)規(guī)范。
傳統(tǒng) JavaScript 代碼的問(wèn)題
讓我們來(lái)看看一般情況下 JavaScript 代碼是如何開(kāi)發(fā)的:通過(guò) <script> 標(biāo)簽來(lái)載入 JavaScript 文件,用全局變量來(lái)區(qū)分不同的功能代碼,全局變量之間的依賴(lài)關(guān)系需要顯式的通過(guò)指定其加載順序來(lái)解決,發(fā)布應(yīng)用時(shí)要通過(guò)工具來(lái)壓縮所有的 JavaScript 代碼到一個(gè)文件。當(dāng) Web 項(xiàng)目變得非常龐大,前端模塊非常多的時(shí)候,手動(dòng)管理這些全局變量間的依賴(lài)關(guān)系就變得很困難,這種做法顯得非常的低效。
AMD 的引入
AMD 提出了一種基于模塊的異步加載 JavaScript 代碼的機(jī)制,它推薦開(kāi)發(fā)人員將 JavaScript 代碼封裝進(jìn)一個(gè)個(gè)模塊,對(duì)全局對(duì)象的依賴(lài)變成了對(duì)其他模塊的依賴(lài),無(wú)須再聲明一大堆的全局變量。通過(guò)延遲和按需加載來(lái)解決各個(gè)模塊的依賴(lài)關(guān)系。模塊化的 JavaScript 代碼好處很明顯,各個(gè)功能組件的松耦合性可以極大的提升代碼的復(fù)用性、可維護(hù)性。這種非阻塞式的并發(fā)式快速加載 JavaScript 代碼,使 Web 頁(yè)面上其他不依賴(lài) JavaScript 代碼的 UI 元素,如圖片、CSS 以及其他 DOM 節(jié)點(diǎn)得以先加載完畢,Web 頁(yè)面加載速度更快,用戶(hù)也得到更好的體驗(yàn)。
CommonJS 的 AMD 規(guī)范中只定義了一個(gè)全局的方法,如清單 1 所示。
清單 1. AMD 規(guī)范
define(id?, dependencies?, factory);該方法用來(lái)定義一個(gè) JavaScript 模塊,開(kāi)發(fā)人員可以用這個(gè)方法來(lái)將部分功能模塊封裝在這個(gè) define 方法體內(nèi)。
id 表示該模塊的標(biāo)識(shí),為可選參數(shù)。
dependencies 是一個(gè)字符串 Array,表示該模塊依賴(lài)的其他所有模塊標(biāo)識(shí),模塊依賴(lài)必須在真正執(zhí)行具體的 factory 方法前解決,這些依賴(lài)對(duì)象加載執(zhí)行以后的返回值,可以以默認(rèn)的順序作為 factory 方法的參數(shù)。dependencies 也是可選參數(shù),當(dāng)用戶(hù)不提供該參數(shù)時(shí),實(shí)現(xiàn) AMD 的框架應(yīng)提供默認(rèn)值為 [“require”,”exports”,“module”]。
factory 是一個(gè)用于執(zhí)行改模塊的方法,它可以使用前面 dependencies 里聲明的其他依賴(lài)模塊的返回值作為參數(shù),若該方法有返回值,當(dāng)該模塊被其他模塊依賴(lài)時(shí),返回值就是該模塊的輸出。
CommonJS 在規(guī)范中并沒(méi)有詳細(xì)規(guī)定其他的方法,一些主要的 AMD 框架如 RequireJS、curl、bdload 等都實(shí)現(xiàn)了 define 方法,同時(shí)各個(gè)框架都有自己的補(bǔ)充使得其 API 更實(shí)用。
回頁(yè)首
RequireJS 簡(jiǎn)介
RequireJS 是一個(gè)非常小巧的 JavaScript 模塊載入框架,是 AMD 規(guī)范最好的實(shí)現(xiàn)者之一。最新版本的 RequireJS 壓縮后只有 14K,堪稱(chēng)非常輕量。它還同時(shí)可以和其他的框架協(xié)同工作,使用 RequireJS 必將使您的前端代碼質(zhì)量得以提升。
目前最新版本的 RequireJS 1.0.8 在 IE 6+、Firefox 2+、Safari 3.2+、Chrome 3+、Opera 10+ 上都工作的很好。
回頁(yè)首
實(shí)戰(zhàn) RequireJS
在 Web 應(yīng)用中使用 RequireJS
在 RequireJS 官方主頁(yè)上下載最新的版本的 require.js,放到 Web 頁(yè)面中。示例的 Web 項(xiàng)目結(jié)構(gòu)圖 1 所示。
圖 1. Web 項(xiàng)目結(jié)構(gòu)
通過(guò)在 index.html 中加入一個(gè) <script> 標(biāo)簽直接引入 require.js 即可。
RequireJS 功能初探
RequireJS 使用起來(lái)非常的簡(jiǎn)單,但是這些功能確是在 Web 前端開(kāi)發(fā)中非常重要的,下面我們來(lái)逐一看看如何使用 RequireJS 的各項(xiàng)功能。
使用 RequireJS 加載 JavaScript 文件
即使您的 Web 前端由于種種歷史原因還沒(méi)有打算進(jìn)行模塊化的設(shè)計(jì),RequireJS 作為 JavaScript 文件的加載器,還是可以幫助您來(lái)完成異步非阻塞的文件加載。
如清單 2 所示,有兩個(gè) JavaScript 文件 a.js 和 b.js,里面各自定義了 myFunctionA 和 myFunctionB 兩個(gè)方法,通過(guò)下面這個(gè)方式可以用 RequireJS 來(lái)加載這兩個(gè)文件,在 function 部分的代碼可以引用這兩個(gè)文件里的方法。
清單 2. 加載 JavaScript 文件
<script src="./js/require.js"></script> <script> require(["./js/a.js", "./js/b.js"], function() { myFunctionA(); myFunctionB(); }); </script>require 方法里的這個(gè)字符串?dāng)?shù)組參數(shù)可以允許不同的值,當(dāng)字符串是以”.js”結(jié)尾,或者以”/”開(kāi)頭,或者就是一個(gè) URL 時(shí),RequireJS 會(huì)認(rèn)為用戶(hù)是在直接加載一個(gè) JavaScript 文件,否則,當(dāng)字符串是類(lèi)似”my/module”的時(shí)候,它會(huì)認(rèn)為這是一個(gè)模塊,并且會(huì)以用戶(hù)配置的 baseUrl 和 paths 來(lái)加載相應(yīng)的模塊所在的 JavaScript 文件。配置的部分會(huì)在稍后詳細(xì)介紹。
這里要指出的是,RequireJS 默認(rèn)情況下并沒(méi)有保證 myFunctionA 和 myFunctionB 一定是在頁(yè)面加載完成以后執(zhí)行的,在有需要保證頁(yè)面加載以后執(zhí)行腳本時(shí),RequireJS 提供了一個(gè)獨(dú)立的 domReady 模塊,需要去 RequireJS 官方網(wǎng)站下載這個(gè)模塊,它并沒(méi)有包含在 RequireJS 中。有了 domReady 模塊,清單 2 的代碼稍做修改加上對(duì) domReady 的依賴(lài)就可以了。
清單 3. 頁(yè)面加載后執(zhí)行 JavaScript
<script src="./js/require.js"></script> <script> require(["domReady!", "./js/a.js", "./js/b.js"], function() { myFunctionA(); myFunctionB(); }); </script>執(zhí)行清單 3 的代碼后,通過(guò) Firebug 可以看到 RequireJS 會(huì)在當(dāng)前的頁(yè)面上插入為 a.js 和 b.js 分別聲明了一個(gè) <script> 標(biāo)簽,用于異步方式下載 JavaScript 文件。async 屬性目前絕大部分瀏覽器已經(jīng)支持,它表明了這個(gè) <script> 標(biāo)簽中的 js 文件不會(huì)阻塞其他頁(yè)面內(nèi)容的下載。
清單 4. RequireJS 插入的 <script>
<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="js/a.js" src="js/a.js"></script>使用 RequireJS 來(lái)定義 JavaScript 模塊
這里的 JavaScript 模塊與傳統(tǒng)的 JavaScript 代碼不一樣的地方在于它無(wú)須訪問(wèn)全局的變量。模塊化的設(shè)計(jì)使得 JavaScript 代碼在需要訪問(wèn)”全局變量”的時(shí)候,都可以通過(guò)依賴(lài)關(guān)系,把這些”全局變量”作為參數(shù)傳遞到模塊的實(shí)現(xiàn)體里,在實(shí)現(xiàn)中就避免了訪問(wèn)或者聲明全局的變量或者函數(shù),有效的避免大量而且復(fù)雜的命名空間管理。
如同 CommonJS 的 AMD 規(guī)范所述,定義 JavaScript 模塊是通過(guò) define 方法來(lái)實(shí)現(xiàn)的。
下面我們先來(lái)看一個(gè)簡(jiǎn)單的例子,這個(gè)例子通過(guò)定義一個(gè) student 模塊和一個(gè) class 模塊,在主程序中實(shí)現(xiàn)創(chuàng)建 student 對(duì)象并將 student 對(duì)象放到 class 中去。
清單 5. student 模塊,student.js
define(function(){ return { createStudent: function(name, gender){ return { name: name, gender: gender }; } }; });清單 6. class 模塊,class.js
define(function() { var allStudents = []; return { classID: "001", department: "computer", addToClass: function(student) { allStudents.push(student); }, getClassSize: function() { return allStudents.length; } }; } );清單 7. 主程序
require(["js/student", "js/class"], function(student, clz) { clz.addToClass(student.createStudent("Jack", "male")); clz.addToClass(student.createStudent("Rose", "female")); console.log(clz.getClassSize()); // 輸出 2 });student 模塊和 class 模塊都是獨(dú)立的模塊,下面我們?cè)俣x一個(gè)新的模塊,這個(gè)模塊依賴(lài) student 和 class 模塊,這樣主程序部分的邏輯也可以包裝進(jìn)去了。
清單 8. 依賴(lài) student 和 class 模塊的 manager 模塊,manager.js
define(["js/student", "js/class"], function(student, clz){ return { addNewStudent: function(name, gender){ clz.addToClass(student.createStudent(name, gender)); }, getMyClassSize: function(){ return clz.getClassSize(); } }; });清單 9. 新的主程序
require(["js/manager"], function(manager) { manager.addNewStudent("Jack", "male"); manager.addNewStudent("Rose", "female"); console.log(manager.getMyClassSize());// 輸出 2 });通過(guò)上面的代碼示例,我們已經(jīng)清楚的了解了如何寫(xiě)一個(gè)模塊,這個(gè)模塊如何被使用,模塊間的依賴(lài)關(guān)系如何定義。還是有一些使用技巧需要提示一下:
- 盡量不要提供模塊的 ID,如 AMD 規(guī)范所述,這個(gè) ID 是可選項(xiàng),如果提供了,在 RequireJS 的實(shí)現(xiàn)中會(huì)影響模塊的可遷移性,文件位置變化會(huì)導(dǎo)致需要手動(dòng)修改該 ID。
- 每個(gè) JavaScript 文件只定義一個(gè)模塊,模塊名稱(chēng)和文件路徑的查找算法決定了這種方式是最優(yōu)的,多個(gè)的模塊和文件會(huì)被優(yōu)化器進(jìn)行優(yōu)化。
- 避免模塊的循環(huán)依賴(lài),如果實(shí)在避免不了,可以模塊中加上對(duì)”require”模塊的依賴(lài),在代碼中直接用 require(”dependencyModuleName”)。
配置 RequireJS
前面的介紹中,我們似乎忽略了一個(gè)基本問(wèn)題,模塊名字是怎么來(lái)的?當(dāng)我在 require 一個(gè)模塊時(shí),這個(gè)模塊是如何映射到具體的 JavaScript 文件上去?這就涉及到如何配置 RequireJS。
最簡(jiǎn)化的加載 RequireJS 的方式如清單 2 所示,在這種情況下,我們沒(méi)有指定一個(gè) baseUrl 和 paths 給 RequireJS,如果通過(guò)如清單 10 所示方式,則 data-main 指定了一個(gè)在當(dāng)前 index.html 目錄并行的文件夾下的 /js/main.js 作為程序入口,而 /js 目錄也將作為 baseUrl 在其他模塊定義時(shí)候使用。
清單 10. 載入 require.js
<script data-main="js/main" src="scripts/require.js"></script>因此,我們前面示例中的所有模塊依賴(lài),都可以去掉”js/”,直接寫(xiě) ”student”, ”class”,”manager” 等。
一種更為直接的方式顯示指定 baseUrl 和 paths 就是利用 require.config 來(lái)設(shè)置這些參數(shù)。如清單 11 所示。
清單 11. 配置 RequireJS
<script type="text/javascript" src="./js/require.js"></script> <script type="text/javascript"> require.config({ baseUrl: "./js", paths: { "some": "some/v1"}, waitSeconds: 10 }); require( ["some/module", "my/module", "./js/a.js"], function(someModule, myModule) {} ); </script>baseUrl指明的是所有模塊的 base URL,比如”my/module”所加載的 script實(shí)際上就是 /js/my/module.js。注意,以 .js 結(jié)尾的文件加載時(shí)不會(huì)使用該 baseUrl,它們?nèi)匀粫?huì)使用當(dāng)前 index.html所在的相對(duì)路徑來(lái)加載,所以仍然要加上”./js/”。如果 baseUrl沒(méi)有指定,那么就會(huì)使用 data-main中指定的路徑。
paths 中定義的路徑是用于替換模塊中的路徑,如上例中的 some/module 具體的 JavaScript 文件路徑是 /js/some/v1/module.js 。
waitSeconds 是指定最多花多長(zhǎng)等待時(shí)間來(lái)加載一個(gè) JavaScript 文件,用戶(hù)不指定的情況下默認(rèn)為 7 秒。
另外一個(gè)重要的配置是 packages,它可以指定其他符合 CommonJS AMD 規(guī)范的目錄結(jié)構(gòu),由此帶來(lái)了豐富的擴(kuò)展性。如 Dojo、jQuery 等的模塊也可以通過(guò)該配置來(lái)讓 RequireJS 加載。
其他可配置的選項(xiàng)還包括 locale、context、deps、callback等,有興趣的讀者可以在 RequireJS 的官方網(wǎng)站查閱相關(guān)文檔。
回頁(yè)首
綜合運(yùn)用 RequireJS
當(dāng) RequireJS 與其他框架一起工作的時(shí)候,顯然它是可以作為統(tǒng)一的加載器來(lái)加載其他框架。鑒于 jQuery、Dojo 等已經(jīng)支持了 AMD ,那么就有可能在定義自己的模塊的時(shí)候,通過(guò)適當(dāng)配置,直接把其他框架的模塊作為依賴(lài)對(duì)象。
RequireJS 和 Dojo
清單 11是一個(gè)基本的 RequireJS 和 Dojo 集成配置方法,這里關(guān)鍵是將 dojo 和 dijit 都在 packages 里面注冊(cè)一下。
清單 12. 配置 RequireJS 和 Dojo
<script> require = { packages: [ { name: “dojo”, location: “dojo”, main:”lib/main-browser”, lib: “.”}, { name: “dijit”, location: “dijit”, main:”lib/main”, lib: “.”} ], paths: { require: “./js”}, ready: function () { require([“my/module”], function (module) { }); } }; </script>在定義 module 這個(gè)模塊時(shí)就可以直接將 dojo 和 dijit 里的模塊作為依賴(lài)對(duì)象了。
RequireJS 和 jQuery
把 jQuery 作為一個(gè)依賴(lài)模塊來(lái)使用也非常簡(jiǎn)單,只需要在 RequireJS 里的 config 里做相應(yīng)的配置就可以了。
清單 13. 配置 RequireJS 和 jQuery
require.config({ paths: { "jquery": "./js/jquery-1.7"} }); require(["jquery"],function(jQ){ console.log(jQ); });RequireJS 和 Web Workers
Web Workers 是多線程的 JavaScript,每個(gè) worker 里面的腳本都 會(huì)啟動(dòng)一個(gè)新的線程來(lái)執(zhí)行,通過(guò)在 worker 里面用 importsScript 來(lái)加載 require.js 可以直接在 worker 里面使用 RequireJS 。
清單 14. RequireJS 和 Web Workers
importScripts('./js/require.js'); require(["require"], function(require) { postMessage("test"); } );參考資料
學(xué)習(xí)
- 學(xué)習(xí) RequireJS,請(qǐng)參考?RequireJS 官方網(wǎng)站。
- 學(xué)習(xí) AMD,請(qǐng)參考?CommonJS AMD 規(guī)范。
- 學(xué)習(xí) Dojo,請(qǐng)參考?Dojo 官方網(wǎng)站。
- 學(xué)習(xí) WebWorkers,請(qǐng)參考?WebWorkers 規(guī)范。
- developerWorks Web development 專(zhuān)區(qū):通過(guò)專(zhuān)門(mén)關(guān)于 Web 技術(shù)的文章和教程,擴(kuò)展您在網(wǎng)站開(kāi)發(fā)方面的技能。
- developerWorks Ajax 資源中心:這是有關(guān) Ajax 編程模型信息的一站式中心,包括很多文檔、教程、論壇、blog、wiki 和新聞。任何 Ajax 的新信息都能在這里找到。
- developerWorks Web 2.0 資源中心,這是有關(guān) Web 2.0 相關(guān)信息的一站式中心,包括大量 Web 2.0 技術(shù)文章、教程、下載和相關(guān)技術(shù)資源。您還可以通過(guò)?Web 2.0 新手入門(mén)?欄目,迅速了解 Web 2.0 的相關(guān)概念。
- 查看?HTML5 專(zhuān)題,了解更多和 HTML5 相關(guān)的知識(shí)和動(dòng)向。
原文鏈接 :http://www.ibm.com/developerworks/cn/web/1209_shiwei_requirejs/
轉(zhuǎn)載于:https://www.cnblogs.com/dragonstreak_1/p/3678048.html
總結(jié)
以上是生活随笔為你收集整理的使用 RequireJS 优化 Web 应用前端的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 不用中间变量交换两个变量的值
- 下一篇: HTML5 Boilerplate -