最近更新了一下腦袋的 ECMAScript 5 -> ECMAScript 6, 發現滿滿的語法糖在我腦袋圍繞著 XD.

正題: 打造Framework 一直是我想做的事之一,原因:

  • 不爽使用流行的框架,自造也不錯
  • 期望達到自己方便、可以改善些效能問題
  • 持續練技術

在手無寸鐵的情況想要攻略大魔王好像異想天開,於是就到處去參考別人的框架,做code review. 大致上整理出幾個會發生的事情(多數是廢話):

  • 除了最主要核心以外,大多一定會使用到別人的模組,無可避免,想自造根本浪費不必要的時間

  • 優化瓶頸,不管是重構還是做除錯,如果能力不足還是可能會讓整個程式出一堆大問題 (練熟 node)

  • 一開始就太過理想,導致想做的東西太多了,沒辦法一次應付完,功能就會修/做到死

所以不管怎樣,該學的還是得學完,最好多去爬爬別人的文章,解決之道。

以下是針對 Web 框架設計所要知道的一些基礎功能、設施:

檔案伺服器

檔案伺服器要能夠訪問 /www 目錄下的內容,包含各式各樣的檔案 (html,mp4,css...)。

這個功能會用到 fs, url, path 原生模組,還有一個 mime 模組,mime 模組是用來處理檔案的 Content-Type。 其實只要解析網址要求的路徑,對應到 /www 資料夾就可以了

let url_path = url.parse(request.url).pathname;  
let realpath = path.join(__dirname,'/www',url_path);  

如此一來就搞定在www底下訪問路徑了

var mime = require('mime');  
fs.readFile(realpath,function(err,file){  
response.writeHead(200,{'Content-Type',mime.lookup(file)})  
response.end(file);  
});

這裡要注意的大概是如果是訪問目錄的檢查,不多寫出來,該方法是 fs.stat ,取得該路徑的狀態(是檔案或目錄型態等等)

另外,你可能會想做到一訪問目錄就尋找看看 index.html , index.htm ,可以用 fs.exists 取得檔案是否存在的狀態

伺服器快取

檔案伺服器的訪問量太高了,這樣會導致在人多的時候同樣也造成回應阻塞,所以必須要有對策,將讀取過的檔案先設定一個變數,並且把資料保存大約 3600 秒(自行控制)

雛形:

var cache = {};  
function regist(type) {  
    if(!cache[type]) {
        cache[type] = {};
    }
}
function get(type, key) {  
    if(cache[type] && cache[type][key]) {
        return cache[type][key];
    }
}
function put(type, key, val) {  
    if(cache[type]) {
        cache[type][key] = val;
    }
}
function del(type, key) {  
    if(cache[type] && cache[type][key]) {
        cache[type][key] = null;
        delete cache[type][key];
    }
}
function query(type, key) {  
    if(cache[type] && cache[type][key]) {
        return true;
    } else {
        return false;
    }
}
module.exports = {  
    regist: regist,
    get: get,
    put: put,
    del: del,
    query: query
};  

這是之前在IT邦幫忙鐵人賽看到的一個非常簡單的快取寫法

好的快取取決於管理機制,你也可以使用現成的 LRU, MRU....等等演算法做到好的快取,但是在這裡,要注意的是,快取也可以連結其他功能的機制,比方說(路由)

程式路由機

寫web框架最主要的服務就是這個路由機程式了,他會處理每個要求路徑的方法、內容,而提供顯示。

最首先要做的就是寫一個可以加入每個要處理的url路徑:

你必須寫個函式可以把 request, response 內容註冊到這個物件中,資料他可能會長這樣:

//使用
ROUTER.get('/showName',function(request,response){

})

//註冊後的情形
let ROUTER = {  
    "/showName":{
        type:"GET",
        func:doShowMyName
    }
}

還有就是,取得每個 request 方法的函式是 request.method 上面省略了很多程式碼,做完之後,也可以寫檔案上傳來試試,另外取得各種方法的參數值,除了 GET 以外, POST 的參數可以在body找到。

最後,使用者寫起來應該會像這樣:

ROUTER.get('/getName',function(request,response){ /*insert code here*/ });  
ROUTER.post('/setName',function(request,response){ /*insert code here*/ });  

模板引擎

模板引擎也是很重要的機制,用於整合後端與前端的程式,開發者可以決定前端應該丟出哪些內容,以及登入後的處理,就會用到 ejs 的模組,當然還有jade之類的可以使用,當模組安裝完畢後,只要用 fs 把ejs檔案丟進去,他就會自動把 html 格式的頁面產生好,給你丟出。

let ejs = require('./ejsparse.js');

let view = new ejs();

view.render('index.ejs',{  
    name:"Foobar"
},function(callback){
    console.log(callback); //response.end(callback)
});

核心內容大概就這樣,不要以為就這樣完結了唷,這時候,可能連一個架構都沒有用上,可以思考一下朝著MVC的架構設計。

另外 https 這個項目,自行用官方的 https 模組處理就可以了,所以不用太多說明,接著你可能會遇到想要建立叢集的心情,不要擔心,因為有模組。

伺服器叢集

叢集運作,一次可以開啟多個行程,幫你分散處理伺服器的活動,使用cluster模組就行了:

let cluster = require('cluster');  
cluster.fork(); //執行幾次 fork 就會開啟幾次行程,但是要注意cpu的數量  

`

接著還有一個額外的內容,就是資料庫,可以分成兩種方式去做,一種是自己寫資料庫,另一種則是手動優化那些SQL資料庫並製作成一個資料庫的驅動,當然你也可以直接就用 MongoDB

自己寫本地資料庫

這挺有趣的,因為連資料庫都能自己寫,好處就是可以快速、方便,好管理,缺點就是功能不太多,不夠強大,即使如此,在簡單的開發下已經很足夠用了。

接著你需要實踐幾個功能

  • 資料庫路徑定義
  • 建立資料庫
  • 建立集合表
  • C.R.U.D (新增,修改,刪除,查詢)

最好有個交互式指令介面可以邊輸入邊開發,讓程式人性化一些,額外多出的功能可以順便做:

  • TCP 網路訪問資料庫
  • 交互式指令操作
  • 支援連結其他 SQL 資料庫
  • 手動升級
  • 加密
  • 網路管理

而實際操作可能會像這樣

let db = require('microdatabase');  
let collection = db.database("WEB_SITE").collection('users');

collection.find().then(function(document){/*insert code here*/}).catch(function(err){/**/})

db.createDatabase("HELLO");  
...

最後,如果再來實踐 RTC 介面或是 web socket, 也可以完善整個框架的功能 (遭打)...