NodeAsp
是一套Classic ASP框架,借鏡了NodeJS的模組化思想,讓您可以用全新的理念愉快地書寫ASP程式。
NodeAsp
使用遵循CommonJS規範的require,完全相容於NodeJS模組載入方式,讓您可以直接使用NodeJS 50%以上的模組。一切不關乎NodeJS運作環境和ES5-ES6特有物件的模組都能直接使用。如此龐大的模組資源庫,這在以往任何ASP框架下都是沒有的。
NodeAsp
是ASP領域獨樹一幟的創新框架,她的出現改變了傳統的ASP編寫模式,讓您只需要會js就可以同時完成前後端開發,並免除了部署NodeJS伺服器的繁瑣過程。
NodeAsp
作為ASP領域的終結者,來了。
NodeAsp:http://nodeasp.com
模組下載:http://nap.webkits.cn
這個框架僅限在WIN平台IIS上運作。通常普通的ASP虛擬主機即可運行本框架。
default.asp
<!-- #include file="NodeAsp.asp" -->
<%
require( ' ./index.js');
%>
index.js
var http = require ( 'http' ) ;
http . createServer ( function ( req , res ) {
res . writeHead ( 200 , { 'Content-Type' : 'text/plain' } ) ;
res . end ( 'Hello Worldn' ) ;
} ) ;
第一步:下載NodeAsp。
第二步:引用NodeAsp。
<!--#include file="NodeAsp.min.asp" -->
第三步:使用NodeAsp。
< %
var version = process.version;
Response.Write(version);
% >
這些物件在所有模組中都是可用的。有些物件實際上並非在全域作用域內,而只是在模組作用域內——這種情況在以下文件中會特別指出。
在瀏覽器中,頂級作用域就是全域作用域。這就是說,在瀏覽器中,如果目前是在全域作用域內,var something將會宣告一個全域變數。在NodeAsp中則不同。頂級作用域並非全域作用域,在NodeAsp模組裡的var something只屬於那個模組。
輸出與運作環境有關的一些資訊。
在NodeAsp中,process存在的主要目的是為了相容於某些NodeJS模組,通常不會使用得到。
用於列印標準輸出和標準錯誤。
詳見下面的控制台
章節。
我們引入了buffer模組來相容於NodeJS的Buffer,請注意必須在node_modules下包含buffer模組才能使用Buffer。
如果您忘記在node_modules下放入buffer模組,這並不會影響程式的正常運行,只有當您使用Buffer時,才會拋出錯誤。
引入模組。與NodeJS有點不一樣的是,因為IIS只能直接執行ASP檔案而非JS文件,所以require也可以用來在ASP程式碼中require一個模組。類似於在命令列中執行node test.js。
如果想知道呼叫require()方法載入模組時的真實檔案路徑,可以使用require.resolve()方法來得到。
詳見下面的模块
章節。
目前所執行程式碼檔案的檔案路徑。這是該程式碼檔案經過解析後的絕對路徑。
例如:執行C:websitesnodeaspindex.asp
// module.js
Response . Write ( __filename ) ;
// C:websitesnodeaspmodule.js
// index.asp
require ( './module' ) ;
Response . Write ( __filename ) ;
// C:websitesnodeaspindex.asp
目前執行腳本所在目錄的目錄名。
當前模組的引用。特別地,module.exports和exports指向同一個物件。 module其實並非全域的而是各模組本地的。
詳見下面的模块
章節。
module.exports物件的引用,該物件被目前模組的所有實例所共享,透過require()可存取該物件。 何時使用exports以及何時使用module.exports的詳情可參考模組系統文件。 exports其實並非全域的而是各個模組本地的。
詳見下面的模块
章節。
定時器函數一共包括以下四個。由於ASP是單線程的,所以以下函數實際上是不相容的。
setTimeout(cb, ms)
clearTimeout(t)
setInterval(cb, ms)
clearInterval(t)
為了更好的偵錯ASP程序,我們實作了類似NodeJS/Chrome瀏覽器的命令列偵錯工具。下載網址:https://github.com/Spikef/NodeAsp-Console
NOTE: 需要IIS7.5和.NET4才能運行,其它環境未測試。
以管理員權限開啟CMD,使用REGASM指令註冊componentTerminal.dll,其中REGASM位於C:WindowsMicrosoft.NETFrameworkv4.0.30319(具體位置與版本號碼有關)。
C:WindowsMicrosoft.NETFrameworkv4.0.30319REGASM D:componentTerminal.dll /codebase
對於32位元系統,找到如下登錄位置:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftInternet ExplorerMAINFeatureControlFEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701
對於64位元系統,找到如下登錄位置:
HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftInternet ExplorerMAINFeatureControlFEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701
選擇或新建項FEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701
,然後新建DWORD值:
名稱:w3wp.exe 值:1
雙擊Console.exe,開啟NodeAsp偵錯命令列。
在default.asp中輸入以下程式碼,然後透過瀏覽器存取default.asp。接下來,你就可以在Console.exe
看到結果了。
var a = { name : "nodeasp" , value : true }
// 在console中将输出
//{
// name: "nodeasp",
// value: true
//}
向Console輸出,使用預設顏色。
var a = { name : "NodeAsp" , version : "0.0.1" } ;
console . log ( a ) ;
向Console輸出,使用綠色。
向Console輸出,使用紅色。特別地,如果輸出的是Error對象,將顯示完整的錯誤訊息。
向Console輸出,使用黃色。
使用label指定名稱,開始一個計時器,用於計算操作所需花費的時間。
輸出某項操作所需要消耗的時間。
Example:
console . time ( '100-elements' ) ;
for ( var i = 0 ; i < 100 ; i ++ ) {
;
}
console . timeEnd ( '100-elements' ) ;
// prints 100-elements: 262ms
在控制台中可以輸入以下命令。
cls/clear:清空偵錯訊息,不可恢復。
about:顯示關於資訊。
copylast:複製上一輸出訊息。
copyall:複製所有輸出資訊。
NodeAsp有一個跟NodeJS幾乎一致的模組載入系統,這樣可以確保NodeAsp可以直接使用大量NodeJS的模組。
NodeAsp的核心幾乎不包含任何開發網站需要功能,所有功能都是透過模組來擴展的。可以透過NodeAsp的模組中心或NPM來尋找您所需的功能模組。
在NodeAsp中,檔案和模組是一一對應的。下面是範例foo.js載入同一目錄下的circle.js。
foo.js的內容
var circle = require ( './circle.js' ) ;
console . log ( 'The area of a circle of radius 4 is '
+ circle . area ( 4 ) ) ;
circle.js的內容:
var PI = Math . PI ;
exports . area = function ( r ) {
return PI * r * r ;
} ;
exports . circumference = function ( r ) {
return 2 * PI * r ;
} ;
circle.js模組輸出了area()和circumference()兩個函數。要輸出某個對象,把它加到exports這個特殊對像下即可。
請注意,exports是module.exports的一個引用,只是為了用起來方便。當你想輸出的是例如建構函式這樣的單一項目,那麼需要使用module.exports。
// 正确输出构造函数
module . exports = MyConstructor ;
模組內的本地變數是私有的。在這個例子中,PI這個變數就是circle.js私有的。
考慮這樣一種情形:
a.js
console . log ( 'a starting' ) ;
exports . done = false ;
var b = require ( './b.js' ) ;
console . log ( 'in a, b.done = %j' , b . done ) ;
exports . done = true ;
console . log ( 'a done' ) ;
b.js
console . log ( 'b starting' ) ;
exports . done = false ;
var a = require ( './a.js' ) ;
console . log ( 'in b, a.done = %j' , a . done ) ;
exports . done = true ;
console . log ( 'b done' ) ;
main.js
console . log ( 'main starting' ) ;
var a = require ( './a.js' ) ;
var b = require ( './b.js' ) ;
console . log ( 'in main, a.done=%j, b.done=%j' , a . done , b . done ) ;
首先main.js載入a.js,接著a.js又去載入b.js。這時,b.js會嘗試去載入a.js。為了防止無限的循環,a.js會回傳一個unfinished copy給b.js。然後b.js就會停止載入,並將其exports物件傳回給a.js模組。
這樣main.js就把這兩個模組都載入完成了。這段程式的輸出如下:
main starting
a starting
b starting
in b , a . done = false
b done
in a , b . done = true
a done
in main , a . done = true , b . done = true
就像NodeJS一樣,通常循環依賴模組並不會導致死循環,但是如果此時直接在模組載入時執行其它模組的方法,會提示找不到對應的方法,所以應該避開這種情況。
// a.js
var b = require ( './b.js' ) ;
exports . add = function ( m , n ) {
console . info ( m + n ) ;
} ;
// b.js
var a = require ( './a' ) ;
var m = 101 , n = 102 ;
exports . result = function ( ) {
a . add ( m , n ) ; // 此处没有问题
} ;
a . add ( m , n ) ; // 此处会报错,找不到a.add方法
如果按檔名沒有查找到,那麼NodeAsp會加上.js
和.json
後綴名,再嘗試載入。
.js
會被解析為Javascript純文字文件, .json
會被解析為JSON格式的純文字檔。
模組以'/'為前綴,則表示絕對路徑。例如,require('/home/marco/foo.js') ,載入的是/home/marco/foo.js這個檔案。
模組以'./'為前綴,則路徑是相對於呼叫require()的檔案。 也就是說,circle.js必須和foo.js在同一目錄下,require('./circle')才能找到。
當沒有以'/'或'./'來指向一個檔案時,這個模組是從node_modules資料夾載入的。
如果指定的路徑不存在,require()會拋出錯誤。
NOTE: 考慮到IIS主機上,
.js
檔案可以直接透過瀏覽器訪問,所以如果不希望洩露原始碼,您也可以使用任意檔案後綴名。
如果require()中的模組名稱不是一個本地模組,也沒有以'/', '../', 或是'./'開頭,那麼node會從當前模組的父目錄開始,嘗試在它的/ node_modules資料夾裡載入對應模組。
如果沒有找到,那麼就再向上移動到父目錄,直到到達頂層目錄位置。
例如,如果位於'/home/ry/projects/foo.js'的檔案呼叫了require('bar.js'),那麼node查找的位置依序為:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
可以把程式和函式庫放到一個單獨的資料夾裡,並提供單一入口來指向它。有三種方法,使一個資料夾可以作為require()的參數來載入。
首先是在資料夾的根目錄建立一個叫做package.json的文件,它需要指定一個main模組。下面是一個package.json檔案的範例。
{
"name" : "some-library" ,
"main" : "./lib/some-library.js"
}
範例中這個文件,如果是放在./some-library目錄下面,那麼require('./some-library')就會去載入./some-library/lib/some-library.js。
如果目錄裡沒有package.json這個文件,那麼node就會嘗試去載入這個路徑下的index.js。
模組在第一次載入後會被快取。這意味著(類似其他緩存)每次調用require('foo')的時候都會返回同一個對象,當然,必須是每次都解析到同一個文件。
多次呼叫require(foo) 未必會導致模組中的程式碼執行多次. 這是一個重要的功能. 借助這個功能, 可以傳回部分完成的物件; 這樣, 傳遞依賴也能被載入, 即使它們可能導致循環依賴。
如果你希望一個模組多次執行,那麼就輸出一個函數,然後呼叫這個函數。
模組的快取是依賴解析後的檔名。由於隨著呼叫的位置不同,可能解析到不同的檔案(例如需從node_modules資料夾載入的情況),所以,如果解析到其他檔案時,就不能保證require('foo')總是會傳回確切的同一對象。
在每一個模組中,變數module 是代表目前模組的物件的參考。 特別地,module.exports 可以透過全域模組物件exports 來取得。 module 不是事實上的全域對象,而更像是每個模組內部的。
module.exports物件是透過模組系統產生的。因此,只需要將要匯出的物件賦值給module.exports
。例如,我們也可以使用下面的方法來寫circle.js,這是完全等效的。
// circle.js
var PI = Math . PI ;
var circle = { } ;
circle . area = function ( r ) {
return PI * r * r ;
} ;
circle . circumference = function ( r ) {
return 2 * PI * r ;
} ;
module . exports = circle ;
用於區別模組的標識符。通常是完全解析後的檔名。
模組完全解析後的檔名。
引入這個模組的模組。
當使用require()引用一個模組時,是依照下列流程根據表達式來找出目標模組的。
require ( X ) from module at path Y
1. If X begins with './' or '/' or '../'
a . LOAD_AS_FILE ( Y + X )
b . LOAD_AS_DIRECTORY ( Y + X )
2. LOAD_NODE_MODULES ( X , dirname ( Y ) )
3. THROW "not found"
LOAD_AS_FILE ( X )
1. If X is a file , load X as JavaScript text . STOP
2. If X . js is a file , load X . js as JavaScript text . STOP
3. If X . json is a file , parse X . json to a JavaScript Object . STOP
LOAD_AS_DIRECTORY ( X )
1. If X / package . json is a file ,
a . Parse X / package . json , and look for "main" field .
b . let M = X + ( json main field )
c . LOAD_AS_FILE ( M )
2. If X / index . js is a file , load X / index . js as JavaScript text . STOP
3. If X / index . json is a file , parse X / index . json to a JavaScript object . STOP
LOAD_NODE_MODULES ( X , START )
1. let DIRS = NODE_MODULES_PATHS ( START )
2. for each DIR in DIRS :
a . LOAD_AS_FILE ( DIR / X )
b . LOAD_AS_DIRECTORY ( DIR / X )
NODE_MODULES_PATHS ( START )
1. let PARTS = path split ( START )
2. let I = count of PARTS - 1
3. let DIRS = [ ]
4. while I >= 0 ,
a . if PARTS [ I ] = "node_modules" CONTINUE
c . DIR = path join ( PARTS [ 0 . . I ] + "node_modules" )
b . DIRS = DIRS + DIR
c . let I = I - 1
5. return DIRS
內建模組與NodeJS一樣,也必須先require才能使用。
此模組用於編寫程式的單元測試案例,透過require('assert')呼叫。直接移植自NodeJS。
事件處理模組,直接移植自NodeJS。
檔案操作模組,相容於絕大部分NodeJS檔案操作模組的同步操作API方法。
HTTP請求與處理模組。移植並修改自NodeJS,絕大部分相容。
本模組包含一套用於處理和轉換檔案路徑的工具集。幾乎所有的方法只做字串變換, 不會呼叫檔案系統檢查路徑是否有效。
移植並修改自NodeJS,幾乎完全相容。
編碼和解碼URL,完全相容NodeJS。
處理URL查詢字串,完全相容於NodeJS。
此模組包含用以URL 解析的實用函數。 使用require('url') 來呼叫該模組。
完全相容NodeJS。
輔助方法模組,相容於4.0以下版本的NodeJS。
編譯NodeAsp原始碼需要安裝node環境,同時需要全域安裝uglifyJS。
在命令列中執行node build
即可,編譯好的檔案位於bundle目錄中。
C:DiskprojectsNodeAsp>node build
-----------------------
# build nodeAsp success
+ build/NodeAsp.min.asp
@ 2016-03-01 13:46:04
-----------------------
MIT