Dojo 提供了一個非常強大的javascript控制項陳列庫.
在使用dojo之前,使用者基本上不需要具備任何基礎知識. 你可以用script遠程連結到dojo(dojo.js), 也可以把dojo.js下載到本地並用script標籤載入.
如果你不太瞭解dojo, 可以參考一下如下資料:
- Dojo 1.4 Cheat Sheet
- Dojo Base Source Tree (1.4.3)
- Dojo Reference Guide
大體上,dojo.js和jquery.js 或者 prototype js, 裡面有很多開發web應用的常用的特性: 包括:
- JavaScript Language Helpers
- Object 工具
- Array 工具
- DOM 操作
- 標準的事件機制
- Ajax & 跨域請
- JSON 工具
- 簡單特效
- 瀏覽器安全色
不僅如此, dojo還有很多其他javascript庫(jquery, ext等等)所不具有的功能. 其中一個很重要的功能就是模組化的機制 - 模組系統(dojo.require()
). JavaScript 和 瀏覽器本身以及其他的javascript庫並不支援這種特性, dojo很好的解決了這種問題.
模組系統
dojo.require('my.module')
用於載入javascript檔案, 功能類似於script標籤的作用.
假設你有一個本地的開發環境,目錄結構如下:(http://localhost:8888
.)
index.html
是一個包含 dojo.js 簡單頁面
.
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”dojo/dojo.js”></script>
</body>
</html>
假設我們要用Flickr API擷取資料, 這時候,我們就要用到跨域請求, 但是這些功能模組並不是在dojo base庫裡面, 我們需要另外載入所需的dojo模組:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script src=”/dojo/io/script.js”></script>
</body>
</html>
這裡我們可以用script標籤解決這種問題, 同樣還有另外一種方式, 這種方式體現了模組系統的宗旨: 我們用dojo.require()
載入 dojo.io.script
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”dojo.io.script”);
//Note: do not include the .js
</script>
</body>
</html>
通過
dojo.require()
我們獲得了一個模組系統, 它提供了一系列我們開發複雜web2.0頁面所需要的組件. 接下來的內容我們會著重介紹模組系統的特性:
首先, dojo.require()會避免重複載入, 如果script指令碼被瀏覽器緩衝了, dojo會調用緩衝的資源從而避免不必要的http請求, 事實上,你可以隨便調用
dojo.require(), 不管調用了多少
dojo.require(), dojo都會保證同樣的模組只會被載入一次.
我們也可以建立自己的模組. 讓我們回到Flickr API的例子, 我們要開發一個用Flickr資料的大型web應用, 我們需要能很好的組織和管理這些javascript代碼. 歸根結底,我們需要建立名為flickrApp的命名空間用於儲存所有該應用的邏輯功能. 為了達到這個目的, 我們更新原有的目錄結構, 建立一個flickrApp.js檔案:
flickrApp.js看起來似乎僅僅是一個js檔案, 但如果你用dojo的眼光來看他, 你會發現他其實應該是一個模組, 為了讓dojo識別他是這個模組, 我們用
dojo.provide()
方法初始化這個js檔案,將其變為一個dojo的模組. 我們加入如下代碼到 flickrApp.js 檔案中:
dojo.provide(”flickrApp “); //similar to doing flickrApp = {};
dojo.provide()
建立了一個以你傳入的字串(flickrApp
)命名的對象結構(名字空間), 我們這裡是建立了一個名為flickrApp的對象, 該對象建立後, 我們便可以像定義該對象的各個屬性一樣來定義該應用(
flickrApp
)
的各個方面, 下面是 flickrApp.js 一個例子:
dojo.provide(”flickrApp”);<br />//start creating the application logic for my flickr app<br />flickrApp.getData = function(){};現在我們可以用
dojo.require()
來載入我們自訂的模組到HTML頁面上:<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”dojo.io.script”);
dojo.require(”flickrApp”);
</script>
</body>
</html>
問題來了, dojo是怎麼知道flickrApp.js模組的檔案系統中存放的位置的呢? 答案就是dojo的路徑管理機制, dojo會根據你傳入
dojo.provide()
的字串來定位該模組的位置,基準點是dojo.js的上一級目錄, 比如dojo.js在
http://localhost:8888/dojo/dojo.js,所以dojo會在
http://localhost:8888/這個目錄層級(dojo.js的上一級)來定位所有的模組。 為了說明這個問題, 我們現在來再一次改變例子的檔案結構,使其看起來
更加具有組織性,如下:
此時,所有的應用相關的代碼都在flickrApp這個目錄(名字空間)下,在這個目錄下,我們可以更進一步將該應用切分成不同的模組。第一個模組就是data.js模組,包含擷取
Flickr資料的邏輯功能,以及跨域,返回資料等等功能。基於這個改變,我們需要在data.js裡面加入
dojo.provide()語句來告訴他新的目錄結構的改變,如下(
data.js
):
//below is similar to doing var flickrApp = {}; flickrApp.data = {};<br />dojo.provide(”flickrApp.data “);<br />// Note: do not include the .js<br />flickrApp.data.getData = function(){};
就像我們之前所說的,dojo會從dojo.js(http://localhost:8888/dojo/dojo.js
)的上一級目錄來開始定位各個模組,所以這裡dojo定位我們的data.js
模組的路徑為http://localhost:8888/flickrApp/data.js。此時,我們的HTML代碼也要做相應的改動:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”dojo.io.script”);
dojo.require(”flickrApp.data
“);
</script>
</body>
</html>
好了,到此為止,我們來想一想,這樣做真的有必要嗎,我們為什麼不能不要藉助這種模組系統,而僅僅是把應用程式所有的邏輯功能都放在一個javascript檔案中呢? 當然可以。但是dojo實現模組系統的目的在於更好更方便的管理代碼,也便於用工具壓縮最佳化代碼。
接下來我們討論一下最重要的部分:依賴管理.
模組可以包含對其他模組的引用,即在模組中可以require()其他的模組, dojo會幫你管理這些模組,讓我們回到我們HTML頁面:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”dojo.io.script”);
dojo.require(”flickrApp.data”);
</script>
</body>
</html>
我們可以看到,這裡我們會在HTML頁面上require 這個dojo.io.script.js模組, 事實上我們可以把這個
require()語句放到
data.js模組裡面,其實這裡
data.js模組是依賴於
dojo.io.script.js模組的,dojo會管理這個依賴關係,所以此時
data.js模組如下:
dojo.provide(”flickrApp.data”);<br />dojo.require(”dojo.io.script”);<br />// Note: dojo.require() should be used after dojo.provide()<br />flickrApp.data.getData = function(){};
此時,我們的HTML頁面就只需要包含一個 require()
語句 (data.js模組)
.
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”flickrApp.data”);
</script>
</body>
</html>
dojo會管理這些依賴關係,確保data.js依賴於
dojo.io.script.js。
更有甚者,依賴管理還需要一個響應通知,即當所有依賴的模組都被載入完成後的一個響應通知。這件事情可以用dojo.ready()這個函數來實現,
dojo.ready()將會註冊一個函數,這個函數會在所有的DOM節點載入完成,並且所有模組及其的相依模組都載入和解析完成時 被調用。用法如下:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/dojo/dojo.js”></script>
<script>
dojo.require(”flickrApp.data”);
dojo.ready(function(){
// Note that dojo.ready() is a shortcut for dojo.addOnLoad() added in Dojo 1.4
// Run code from data.js and all its dependencies safely
});
</script>
</body>
</html>
dojo.ready()
可以用在任何時間,任何地方,甚至是dojo.ready()的callback方法裡面,比如
:
dojo.require(”some.module”);<br />dojo.ready(function(){<br />//run code from some.module.js and all its dependencies safely<br />dojo.require(’some.other.module’);<br />dojo.ready(function(){<br />//run code from some.other.module.js and all its dependencies safely<br />});<br />});
就像前面提到的,當用到dojo的 require()時,dojo會從dojo.js的上一級目錄開始尋找,比如
require('some.other.module')會從
some/other/module.js開始尋找:
其實dojo也提供了我們定義dojo的預設尋找路徑的方式,我們再更新一下我們應用程式的結構,如下:
可以看到,我們的模組 - some.other.module.js已經在dojo預設的模組尋找路徑之外了,我們要告訴dojo這個結構的變化就要用到
dojo.registerModulePath():
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script src=”/js/dojo1.4.1/dojo.js”></script>
<script>
dojo.registerModulePath(”some”, “../../some/”);
dojo.require(”some.other.module”);
</script>
</body>
</html>
通過dojo.registerModulePath("some", "../../some/")
語句,dojo便知道這個使用者自訂的模組的位置,這裡,我們告訴dojo我們的自訂模組some是在dojo.js的上兩級(../../some
),這個時候,dojo便知道了如何去解析這個名字空間(some.other.module
)並載入some.other.module.js檔案了。此時此刻,dojo便能識別所有在“
../../some/
”路徑下的自訂模組。之所以需要這樣做的原因主要是安全問題,瀏覽器是不支援javascript訪問檔案系統的。 如果你對
djConfig
比較瞭解的話,你會發現其實這個對象也可以用來做“dojo.registerModulePath()
”的工作:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”utf-8″ />
<title>Dojo</title>
</head>
<body>
<script type=”text/javascript”>
var djConfig = {modulePaths:{”some”:”../../some/”}};
</script>
<script src=”/dojo/dojo1.4.1/dojo.js”></script>
<script>
dojo.require(”some.other.module”);
</script>
</body>
</html>
其實dojo關於模組系統的內容還有很多,這裡我們主要是基於本地的dojo,其實dojo還可以以跨域的方式載入(從AOL或者Google CDN),所以模組系統也會去支援這種特性。模組系統的底層實現是基於XHR去請求模組內容的,這些在你用dojo的CDN版本是會改變,這時模組系統會轉換到跨域的方式(基於script元素)。
Dojo之外
其實這些模組系統的想法也可以在沒有dojo的情況下使用。 YUI3.0也有一個類似的實現,同樣,也有一些針對模組系統的想法而專門實現的獨立的控制項陳列庫,其中之一就是RequireJS
,他是基於dojo實現的。有興趣不妨下載下來研究一下。