詳解JavaScript模組化開發
什麼是模組化開發?
前端開發中,起初只要在script標籤中嵌入幾十上百行程式碼就能實現一些基本的互動效果,後來js得到重視,應用也廣泛起來了,jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得前端開發得到重視,也使得前端專案越來越複雜,然而,JavaScript卻沒有為組織程式碼提供任何明顯幫助,甚至沒有類的概念,更不用說模組(module)了,那麼什麼是模組呢?
一個模組就是實現特定功能的檔案,有了模組,我們就可以更方便地使用別人的程式碼,想要什麼功能,就載入什麼模組。模組開發需要遵循一定的規範,否則就都亂套了。
根據AMD規範,我們可以使用define定義模組,使用require呼叫模組。
目前,通行的js模組規範主要有兩種:CommonJS和AMD。
AMD規範
AMD 即Asynchronous Module Definition,中文名是“非同步模組定義”的意思。它是一個在瀏覽器端模組化開發的規範,伺服器端的規範是CommonJS
模組將被非同步載入,模組載入不影響後面語句的執行。所有依賴某些模組的語句均放置在回撥函式中。
AMD 是 RequireJS 在推廣過程中對模組定義的規範化的產出。
define() 函式
AMD規範只定義了一個函式 define,它是全域性變數。函式的描述為:
define(id?, dependencies?, factory);
引數說明:
id:指定義中模組的名字,可選;如果沒有提供該引數,模組的名字應該預設為模組載入器請求的指定指令碼的名字。如果提供了該引數,模組名必須是“頂級”的和絕對的(不允許相對名字)。 依賴dependencies:是一個當前模組依賴的,已被模組定義的模組標識的陣列字面量。 依賴引數是可選的,如果忽略此引數,它應該預設為["require", "exports", "module"]。然而,如果工廠方法的長度屬性小於3,載入器會選擇以函式的長度屬性指定的引數個數呼叫工廠方法。 工廠方法factory,模組初始化要執行的函式或物件。如果為函式,它應該只被執行一次。如果是物件,此物件應該為模組的輸出值。
模組名的格式
模組名用來唯一標識定義中模組,它們同樣在依賴性陣列中使用:
模組名是用正斜槓分割的有意義單詞的字串 單詞須為駝峰形式,或者".",".." 模組名不允許副檔名的形式,如“.js” 模組名可以為 "相對的" 或 "頂級的"。如果首字元為“.”或“..”則為相對的模組名 頂級的模組名從根名稱空間的概念模組解析 相對的模組名從 "require" 書寫和呼叫的模組解析
使用 require 和 exports
建立一個名為"alpha"的模組,使用了require,exports,和名為"beta"的模組:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb(); } });
require API 介紹: https://github.com/amdjs/amdjs-api/wiki/require
AMD規範中文版:https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)
目前,實現AMD的庫有RequireJS 、curl 、Dojo 、Nodules 等。
CommonJS規範
CommonJS是伺服器端模組的規範,Node.js採用了這個規範。Node.JS首先採用了js模組化的概念。
根據CommonJS規範,一個單獨的檔案就是一個模組。每一個模組都是一個單獨的作用域,也就是說,在該模組內部定義的變數,無法被其他模組讀取,除非定義為global物件的屬性。
輸出模組變數的最好方法是使用module.exports物件。
var i = 1; var max = 30; module.exports = function () { for (i -= 1; i++ < max; ) { console.log(i); } max *= 1.1; };
上面程式碼通過module.exports物件,定義了一個函式,該函式就是模組外部與內部通訊的橋樑。
載入模組使用require方法,該方法讀取一個檔案並執行,最後返回檔案內部的module.exports物件。
CommonJS 規範:http://javascript.ruanyifeng.com/nodejs/commonjs.html
RequireJS和SeaJS
RequireJS由James Burke建立,他也是AMD規範的創始人。
define方法用於定義模組,RequireJS要求每個模組放在一個單獨的檔案裡。
RequireJS 和 Sea.js 都是模組載入器,倡導模組化開發理念,核心價值是讓 JavaScript 的模組化開發變得簡單自然。
SeaJS與RequireJS最大的區別:
SeaJS對模組的態度是懶執行, 而RequireJS對模組的態度是預執行
不明白?看這篇圖文並茂的文章吧:http://www.douban.com/note/283566440/
RequireJS API:http://www.requirejs.cn/docs/api.html
RequireJS的用法:http://www.ruanyifeng.com/blog/2012/11/require_js.html
為什麼要用requireJS
試想一下,如果一個網頁有很多的js檔案,那麼瀏覽器在下載該頁面的時候會先載入js檔案,從而停止了網頁的渲染,如果檔案越多,瀏覽器可能失去響應。其次,要保證js檔案的依賴性,依賴性最大的模組(檔案)要放在最後載入,當依賴關係很複雜的時候,程式碼的編寫和維護都會變得困難。
RequireJS就是為了解決這兩個問題而誕生的:
(1)實現js檔案的非同步載入,避免網頁失去響應; (2)管理模組之間的依賴性,便於程式碼的編寫和維護。
RequireJS檔案下載:http://www.requirejs.cn/docs/download.html
AMD和CMD
CMD(Common Module Definition) 通用模組定義。該規範明確了模組的基本書寫格式和基本互動規則。該規範是在國內發展出來的。AMD是依賴關係前置,CMD是按需載入。
在 CMD 規範中,一個模組就是一個檔案。程式碼的書寫格式如下:
define(factory);
factory 為函式時,表示是模組的構造方法。執行該構造方法,可以得到模組向外提供的介面。factory 方法在執行時,預設會傳入三個引數:require、exports 和 module:
define(function(require, exports, module) { // 模組程式碼 });
require是可以把其他模組匯入進來的一個引數,而export是可以把模組內的一些屬性和方法匯出的。
CMD規範地址:https://github.com/seajs/seajs/issues/242
AMD 是 RequireJS 在推廣過程中對模組定義的規範化產出。 CMD 是 SeaJS 在推廣過程中對模組定義的規範化產出。
對於依賴的模組,AMD 是提前執行,CMD 是延遲執行。
AMD:提前執行(非同步載入:依賴先執行)+延遲執行 CMD:延遲執行(執行到需載入,根據順序執行)
CMD 推崇依賴就近,AMD 推崇依賴前置。看如下程式碼:
// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... }) // AMD 預設推薦的是 define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ... })
另外一個區別是:
AMD:API根據使用範圍有區別,但使用同一個api介面 CMD:每個API的職責單一
AMD的優點是:非同步並行載入,在AMD的規範下,同時非同步載入是不會產生錯誤的。
CMD的機制則不同,這種載入方式會產生錯誤,如果能規範化模組內容形式,也可以
jquery1.7以上版本會自動模組化,支援AMD模式:主要是使用define函式,sea.js雖然是CommonJS規範,但卻使用了define來定義模組
所以jQuery已經自動模組化了
seajs.config({ 'base':'/', 'alias':{ 'jquery':'jquery.js'//定義jQuery檔案 } });
define函式和AMD的define類似:
define(function(require, exports, module{ //先要載入jQuery的模組 var $ = require('jquery'); //然後將jQuery物件傳給外掛模組 require('./cookie')($); //開始使用 $.cookie方法 });
sea.js如何使用?
-
引入sea.js的庫
-
如何變成模組?
-
define
-
-
3.如何呼叫模組?
-exports -sea.js.use
-
4.如何依賴模組?
-require <script type="text/javascript"> define(function (require,exports,module) { //exports : 對外的介面 //requires : 依賴的介面 require('./test.js');//如果地址是一個模組的話,那麼require的返回值就是模組中的exports })
</script>
sea.js 開發例項
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>滑鼠拖拽的模組化開發實踐</title> <style type="text/css"> #div1{ width:200px; height:200px; background:black; position:absolute; display:none;} #div2{ width:30px; height:30px; background:yellow; position:absolute; bottom:0; right:0;} #div3{ width:100px; height:100px; background:blue; position:absolute; right:0; top:0;} </style> <script type="text/javascript" src="./sea.js"></script> <script type="text/javascript"> //A同事 : seajs.use('./main.js'); </script> </head> <body> <input type="button" value="確定" id="input1" /> <div id="div1"> <div id="div2"></div> </div> <div id="div3"></div> </body> </html>
A同事
//A同事寫的main.js: define(function (require,exports,module) { var oInput = document.getElementById('input1'); var oDiv1 = document.getElementById('div1'); var oDiv2 = document.getElementById('div2'); var oDiv3 = document.getElementById('div3'); require('./drag.js').drag(oDiv3); oInput.onclick = function () { oDiv1.style.display = 'block'; require('./scale.js').scale(oDiv1,oDiv2); require.async('./scale.js', function (ex) { ex.scale(oDiv1,oDiv2); }) } });
B同事
//B同事寫的drag.js: define(function(require,exports,module){ function drag(obj){ var disX = 0; var disY = 0; obj.onmousedown = function(ev){ var ev = ev || window.event; disX = ev.clientX - obj.offsetLeft; disY = ev.clientY - obj.offsetTop; document.onmousemove = function(ev){ var ev = ev || window.event; var L = require('./range.js').range(ev.clientX - disX , document.documentElement.clientWidth - obj.offsetWidth , 0 ); var T = require('./range.js').range(ev.clientY - disY , document.documentElement.clientHeight - obj.offsetHeight , 0 ); obj.style.left = L + 'px'; obj.style.top = T + 'px'; }; document.onmouseup = function(){ document.onmousemove = null; document.onmouseup = null; }; return false; }; } exports.drag = drag;//對外提供介面 });
C同事
//C同事寫的scale.js: define(function(require,exports,module){ function scale(obj1,obj2){ var disX = 0; var disY = 0; var disW = 0; var disH = 0; obj2.onmousedown = function(ev){ var ev = ev || window.event; disX = ev.clientX; disY = ev.clientY; disW = obj1.offsetWidth; disH = obj1.offsetHeight; document.onmousemove = function(ev){ var ev = ev || window.event; var W = require('./range.js').range(ev.clientX - disX + disW , 500 , 100); var H = require('./range.js').range(ev.clientY - disY + disH , 500 , 100); obj1.style.width = W + 'px'; obj1.style.height = H + 'px'; }; document.onmouseup = function(){ document.onmousemove = null; document.onmouseup = null; }; return false; }; } exports.scale = scale; });
D同事
// D同事的range.js--限定拖拽範圍 define(function(require,exports,module){ function range(iNum,iMax,iMin){ if( iNum > iMax ){ return iMax; } else if( iNum < iMin ){ return iMin; } else{ return iNum; } } exports.range = range; });
requirejs開發例項
require.config是用來定義別名的,在paths屬性下配置別名。然後通過requirejs(引數一,引數二);引數一是陣列,傳入我們需要引用的模組名,第二個引數是個回撥函式,回撥函式傳入一個變數,代替剛才所引入的模組。
main.js檔案
//別名配置 requirejs.config({ paths: { jquery: 'jquery.min' //可以省略.js } }); //引入模組,用變數$表示jquery模組 requirejs(['jquery'], function ($) { $('body').css('background-color','red'); });
引入模組也可以只寫require()。requirejs通過define()定義模組,定義的引數上同。在此模組內的方法和變數外部是無法訪問的,只有通過return返回才行.
define 模組
define(['jquery'], function ($) {//引入jQuery模組 return { add: function(x,y){ return x + y; } }; });
將該模組命名為math.js儲存。
main.js引入模組方法
require(['jquery','math'], function ($,math) { console.log(math.add(10,100));//110 });
沒有依賴
如果定義的模組不依賴其他模組,則可以:
define(function () { return { name: "trigkit4", age: "21" } });
AMD推薦的風格通過返回一個物件做為模組物件,CommonJS的風格通過對module.exports或exports的屬性賦值來達到暴露模組物件的目的。
相關文章
- Javascript模組化開發基礎JavaScript
- 為什麼JavaScript需要模組化開發?JavaScript
- JavaScript模組化JavaScript
- javascript模組化發展歷程JavaScript
- Javascript 模組化指北JavaScript
- 模組化開發(二)
- 前端模組化開發前端
- Spring Boot + Maven 多模組專案開發詳解Spring BootMaven
- 前端模組化詳解(完整版)前端
- Laravel RESTFul API 模組化開發解決方案LaravelRESTAPI
- javascript模組化簡介JavaScript
- JavaScript 模組化前世今生JavaScript
- JavaScript 中的模組化JavaScript
- JavaScript 模組化總結JavaScript
- JavaScript模組化規範JavaScript
- 淺談模組化開發
- 模組化開發淺析
- 聊聊前端模組化開發前端
- 元件化開發和模組化開發概念辨析元件化
- python模組詳解Python
- matplotlib模組詳解
- difflib模組詳解
- psutil模組詳解
- webpack+jquery 元件化、模組化開發的解決方案WebjQuery元件化
- JavaScript模組化演化史JavaScript
- JavaScript模組化原理淺析JavaScript
- javascript 模組化程式設計JavaScript程式設計
- JavaScript模組化的演變JavaScript
- Js模組化開發的理解JS
- Android模組化開發實踐Android
- 藉助Gradle Plugin解決模組化開發中模組如何對外暴露介面GradlePlugin
- threeJs模組化開發解決方案 import-three-examplesJSImport
- 紅外模組詳解
- lms框架模組詳解框架
- JavaScript 模組的發展史JavaScript
- php+mysql+cookie+模組化開發PHPMySqlCookie
- springboot模組化開發專案搭建Spring Boot
- 利用 Transform 解決模組化開發服務呼叫問題ORM
- 【JavaScript】淺談前端模組化與元件化JavaScript前端元件化