2.4Dojo和Node.js
原文地址:https://dojotoolkit.org/documentation/tutorials/1.10/node/index.html
GitBook地址:https://www.gitbook.com/book/limeng1900/dojo1-11-tutorials-translation-in-chinese/details
轉載請註明出處:http://blog.csdn.net/taijiedi13/ – 碎夢道
JavaScript不僅僅用在客戶端。隨着Node.js這樣的技術的出現,服務器端JavaScript成爲一個很好的選擇。不止JavaScript能用於服務器端,AMD和Dojo Toolkit也可以。本教程主要關於如何在Node.js下使用Dojo。
入門
你應該已經熟悉一些Dojo基礎了,比如 Defining Modules和 Classy JavaScript with dojo/_base/declare 。另外你也應該對 Node.js 有了一定的熟悉,並在你本地安裝了Node.js和npm包管理工具。開始之前你還需要一個Dojo的本地源代碼((Dojo Toolkit SDK)。我們將利用Dojo1.8引進的一些特性,所以確保你用的是最新的發行版本。
另一方面,我準備了一個GitHub庫,它包含了一個子模塊,可以將正確的Dojo部分引入到項目中。它也遵循我們在這個教程中建議的文件佈局和目錄。
Dojo的Built distributions或者CDN distributions 在Node.js不起作用,因爲他們是基於你在使用瀏覽器的用戶代理的假設生成的。生成和已壓縮的代碼也會讓你的服務器端應用調試變得異常困難,所以還是堅持用源碼distribution。
AMD
你或許會對自己說“AMD是給客戶端準備的,我爲什麼要讓它在服務器端運行?”。相比客戶端,服務器端沒那麼需要AMD,AMD在服務器端也沒有劣勢並且開銷很小。這也意味着你可以在客戶端和服務器端使用相同的模塊和編碼風格。使用同一種語言不會讓你走的更遠,但是如果你只用一種代碼風格,就可以更快地編寫代碼。
遺憾的是對於開發人員來說,如果想要使用自己的包和模塊同時在服務器端和瀏覽器端運行,必須同時支持CommonJS packaging和模塊以及AMD模塊。越來越多的包同時支持 CommonJS 和AMD,將來說不定會成爲標準。
好消息是Dojo AMD加載器在Node.js下運行很好,只需要一點工作量,你就可以在 AMD/Dojo可用環境下開始編程。
Bootstrapping
爲了在NodeJS下加載AMD模塊,我們需要一個模塊加載器。Node.js默認的require()只能加載CommonJS模塊。Dojo AMD加載器(還有其它加載器,像RequireJS)可以 “bootstrapped”來讓你擁有一個“AMD環境”。
但是,在實際加載加載器之前,我們需要組織應用的結構。我建議你的程序使用以下佈局:
下面我們要做的就是進一步配置Dojo加載器,讓它可以加載另一個模塊。一旦我們加載了Dojo加載器,它會進行接管,然後加載的任何模塊都會以AMD模塊一樣的方式運行。下面看看應該在叫做/server.js
自引導文件裏放些什麼:
// Configuration Object for Dojo Loader:
dojoConfig = {
baseUrl: "src/", // Where we will put our packages
async: 1, // We want to make sure we are using the "modern" loader
hasCache: {
"host-node": 1, // Ensure we "force" the loader into Node.js mode
"dom": 0 // Ensure that none of the code assumes we have a DOM
},
// While it is possible to use config-tlmSiblingOfDojo to tell the
// loader that your packages share the same root path as the loader,
// this really isn't always a good idea and it is better to be
// explicit about our package map.
packages: [{
name: "dojo",
location: "dojo"
},{
name: "app",
location: "app"
},{
name: "app-server",
location: "app-server"
}]
};
// Now load the Dojo loader
require("./src/dojo/dojo.js");
加載模塊
現在我們可以運行這個文件,但其實它並不會做什麼,因爲還沒有加載任何模塊或執行任何功能代碼。那麼,再來創建一個名叫src/app-server/server.js
的簡單模塊,它會在控制檯記錄一條信息。
require([], function(){
console.log("Hello World!");
});
我們也需要引導腳本來加載這個模塊,最簡單的方法是將模塊包含在配置信息的deps
裏。我喜歡用更容易修改和配置的方法:
// The module to "bootstrap"
var loadModule = "app-server/server";
// Configuration Object for Dojo Loader:
dojoConfig = {
baseUrl: "src/", // Where we will put our packages
async: 1, // We want to make sure we are using the "modern" loader
hasCache: {
"host-node": 1, // Ensure we "force" the loader into Node.js mode
"dom": 0 // Ensure that none of the code assumes we have a DOM
},
// While it is possible to use config-tlmSiblingOfDojo to tell the
// loader that your packages share the same root path as the loader,
// this really isn't always a good idea and it is better to be
// explicit about our package map.
packages: [{
name: "dojo",
location: "dojo"
},{
name: "app",
location: "app"
},{
name: "app-server",
location: "app-server"
}],
deps: [ loadModule ] // And array of modules to load on "boot"
};
// Now load the Dojo loader
require("./src/dojo/dojo.js");
現在我們只需要在項目的根目錄下運行應用:
$ node server
Hello World!
$
上面提到的樣板也有一個名叫
boot.js
的簡便工具,用來從命令行引導模塊。例如,你可以運行node boot "app-server/server"
來運行服務。
使用Node.js模塊
一旦你進入AMD的世界,你就可以用AMD的方式來require()和define()。你可以像客戶端那樣加載模塊。當你使用Node.js使用的標準CommonJS模塊時,挑戰隨之而來。由於require()
以AMD重寫了,所以只調用require()
不再能加載CommonJS模塊。Dojo有相應的解決方案,在Dojo1.8之後,有一個名叫dojo/node
的AMD插件模塊可以用來加載CommonJS模塊。
插件的參數(!後面的字符串)就是當你使用原生Node.js require()的模塊。例如:
// This:
var fs = require("fs");
// Would become:
require([ "dojo/node!fs" ], function(fs){
// Utilise the "fs" module
});
讓我們修改app-server/server.js
來加載Node.js fs
模塊,讀取一個JSON文件,解析並記錄到控制檯:
require([
"dojo/node!fs"
], function(fs){
var helloworld = fs.readFileSync("helloworld.json");
console.log(JSON.parse(helloworld));
});
在項目的根目錄,創建·require([
“dojo/node!fs”
], function(fs){
var helloworld = fs.readFileSync(“helloworld.json”);
console.log(JSON.parse(helloworld));
});·
{
"Hello": "World!"
}
然後再次運行代碼,會得到下面的結果:
$ node server
{ Hello: 'World!' }
$
加載模塊的
dojo/node
和調用node
使用的文件系統都需要路徑。所以雖然我們的模塊./app-server/server.js
,我們還是相對於項目的根目錄來使用路徑。例外的是define()
的相對路徑,它和瀏覽器裏一樣,是相對於模塊的路徑。基於Google V8 JavaScript引擎的Node.js是完全遵守ES5的,就是說你不用擔心你代碼的解碼。例如,在上面,我們不像在瀏覽器裏一樣使用
dojo/json
模塊來解析JSON字符串。不過如果你想要你的代碼在老式瀏覽器上正常工作,你當然可以繼續在代碼中使用這些功能。
現在我們開始在Node.js下使用Dojo加載器來運行AMD模塊了。但是除非我們以Dojo的方式來改變我們的編碼風格,否則並不會從中獲益。不只是在你的服務器端和客戶端共享同樣的代碼,你還可以利用 Dojo Promises、 Dojo Requests、 Dojo i18n、 Dojo Topics 和Events 等其它豐富的AMD模塊。
Deferred and Promises
Node.js的一個強大特性是它的非阻塞性質。Node.js絕大多數的核心API是異步運行的。儘管如此,通過異步回調函數仍是默然方式。如果你用Dojo工作了一段時間之後,你就會了解Deferreds 和 Promises實現的異步功能的強大處理能力。還有一些其它框架爲基於Node.js接口提供一個promise ,但是由於Dojo已經有一個promise框架,我們就用它了。
上面,我們用fs.readFileSync
來加載我們的文件並顯示結果,但是這個方法同步運行並且會阻塞。爲什麼不來個異步的:
require([
"dojo/node!fs"
], function(fs){
fs.readFile("helloworld.json", function(err, data){
if(err) console.error("I was robbed!");
console.log(JSON.parse(data));
});
});
它正常運行,不過我們用非常不Dojo的回調函數作爲結束,再把它變成返回promise的:
require([
"dojo/node!fs",
"dojo/Deferred"
], function(fs, Deferred){
function readFilePromise(filename){
var dfd = new Deferred();
fs.readFile(filename, function(err, data){
if(err) dfd.reject(err);
dfd.resolve(data);
});
return dfd.promise;
}
readFilePromise("helloworld.json").then(function(helloworld){
console.log(JSON.parse(helloworld));
}, function(err){
console.error("I was robbed!");
});
});
現在配合readFilePromise
,我們可以使用promise模式來處理異步函數。這包括改變promise的能力,使代碼更加可讀,而如果你用Node.js異步模塊你就只能“在回調裏回調”。
實際上,你不會每次都創建一個函數來作爲promise使用它。你可是創建一個模塊來exposes 你想要返回promise的函數,然後加載這個模塊來使用這個函數的promise版本。
我已經創建了一個包,叫做setten這裏寫鏈接內容,它提供很多的“開箱即用”功能。它和Dojo Toolkit擁有一樣的許可,而且全部是由Dojo基金會的CLA貢獻的。希望它會隨時間繼續成長,提供更多的讓Node.js下的工作更像Dojo的可用模塊。特別的,一個名爲
setten/util
的模塊可以通過使用util.asDeferred()
將異步回調函數轉換成基於promise的。
構建應用
現在我們有了一些基本的方式,可能你閱讀本教程是想要在Node.js裏創建一個客戶端/服務器端組合應用,就是在服務器端和客戶端都使用Dojo風格的代碼。但是構建完整的應用程序超出了本教程的範圍,我會試着給你一些建議和結構。
第一件要做的事就是爲你已部署應用的客戶端代碼進行優化生成,這樣你的服務器端可以提供一個建立層文件和串聯的CSS。這就是爲什麼我建議你爲應用程序的特定代碼建立三個不同的包:
src/app
- 在服務器端和客戶端共享的模塊。src/app-server
- 你的基礎服務模塊和其它值用在服務器端的模塊。src/app-client
- 你的客戶端模塊。
你可以爲app
和app-client
包創建一個Dojo生成配置文件,這樣它們就可以用Dojo生成器來生成,你還可以創建一個整體應用的生成配置文件用來識別你的層,它們都將有你的服務器端提供。我上面提到的樣本文件提供了這個框架,可以在構建過程中加入一個 build.sh
和build.bat
來生成。構建將會輸出到lib
路徑。
另外一個方法是使用[dojo-boilerplate](https://github.com/csnover/dojo-boilerplate)
項目提供的方法,它可以讓客戶端和服務器端代碼在同一個模塊裏共存。這個方法裏,dojo/has
用來分離客戶端和服務器端代碼。例如,我們可以編寫一個模塊,它在客戶端加載和在服務器端的node下加載時會做分別完全不同的事情:
define([
"dojo/has", // Dojo's feature detection module
"require" // The "context" require, that is aware of the current context
], function(has, require){
var app = {};
if(has("host-browser")){
// we are running under a browser
require([ "dojo/request" ], function(request){
request.get("/helloworld.json", {
handleAs: "json"
}).then(function(data){
console.log(data);
});
});
}
if(has("host-node")){
// we are running under node
require([ "dojo/node!fs" ], function(fs){
var data = fs.readFileSync("helloworld.json");
console.log(JSON.parse(data));
});
}
return app;
});
這些代碼由Dojo生成器生成時,如果我們用Google Closure 來優化代碼,會移除無效分支,就是說服務器端代碼會從模塊中移除。
就我個人而言,我更關注對同時用在服務器端和客戶端的模塊的分離,它可以更“乾淨”地生成,而不是完全依賴於代碼優化來確保服務器端的代碼不會分發給客戶端。雖然這種替代模式是完全可行的。
代碼生成的更多細節見 Creating Builds教程。
小結
希望這個教程可以給你一個在Node.js下使用Dojo的總體印象。我個人認爲在服務器端和客戶端使用相同的編程語言和模式大有可爲。在其他技術和語言都在試圖模糊客戶端和服務器端間的界限時,我認爲我們開始看到這樣的情況——我們的代碼可以真正的實現同構,而不用再擔心執行它的特定上下文。
這裏有一些我或者其他人在Node.js下做的事情,或許能給你一些啓發。我個人已經在NodeJS下用Dojo代替了所有的服務器端代碼。
- 構建“全棧”網站和全棧應用。(My personal homepage)
- 文檔服務器和文檔生成工具。(Dojoment)
- 自動化測試。(Teststack)
- 服務器端widget渲染。(Server Side Dijit)
在NodeJS下用Dojo可以做無數的事。祝你好運,看看你能想到什麼有趣的事情可以做。