去年年初對 Node.js 比較感興趣,也用了很多 Node.js 的框架,但是開發體驗不是特別好,我之前也是後端轉前端,然後再接觸 Node.js ,所以用過挺多的服務端框架,相對js而言,設計一款服務端框架並不容易,本人也不太願意使用 typescript (為什麼不用java,請勿吐槽)編寫並且基於ES6 對入門的小夥伴會更友好一些,然後自己動手開發了一個Node.js 的Web 框架,快過年了才有時間寫文章(手動狗頭),在這裡給大家分享一下開發經歷。
注:目錄只是為了好看,想到什麼寫什麼,沒有文筆可言,小白文。
選型
對於框架底層,想過自己開發一套(成本太高,並且考慮到生態問題)被我否決了,然後比較了 Koa2 與 Express 最終選擇Koa2作為預設底層(最後由於框架的架構設計,koa2服務或者express都可以作為底層庫?),不過最後還是選擇了 koa2 預設整合。
既然選擇了 koa2 那自然也是相容 koa2 的生態圈
架構設計
剛開始開發的時候其實是順著koa的路子走的, 以koa作為底層,對koa的ctx進行擴充套件,後來覺得這樣子封裝一個koa的全家桶貌似沒什麼意義(晚上一大堆,造輪子沒什麼意義,和別的框架有啥區別,請原諒我這老土的想法?),然後開始思考?:做這個框架的初衷和意義。
作為一個後端過來的,自然就想到了用IOC容器作為底層更優雅,但是js並沒有型別約束,介面等特性,也看過很多 typescript的實現(和其他後端框架並無明顯區別,不是我吐槽,其他語言的更完善更安全效能更好),我下定決心要寫一個js(ES6)版的出來(學習成本低,更好入門?就是這麼自以為是),然後就這樣開啟了我的 Node.js 之旅。
寫偏了。。下面介紹一下這個框架的架構:
以容器作為底層,應用類整合容器基類並繫結在容器中,是應用程式物件也是容器的保姆。
其他所有的服務(包括 koa 、router、logger、validate、request、response等等)都是以提供者的形式在應用程式中註冊(實際繫結到了容器)。
容器開發
前期沒想那麼多,開發容器也很順利,在設計依賴注入模式的時候(由於沒有介面),想躲過很多方案,最後決定使用裝飾器(真香),不是ts,使用ECMA草案中裝飾器(使用 babel
轉碼),最後1.0
定稿以後,會成為可選方案,裝飾器可以增加開發體驗,但不是必須的,並且強烈推薦的模式進行設計。
例如我們開放一個端點(路由),裝飾器例子,不是注入:
@Router('users')
class UserController {
@Get()
index() {
// ...
}
}複製程式碼
這種模式進行開發,上述例子開放了一個 GET /users
的訪問端點
那如果進行注入呢:
class UserController {
@Config() config;
@Request()
index(request) {
this.request.param()
this.config.get('app.port')
}
}複製程式碼
我們可以對屬性,方法,建構函式進行注入
原理是使用裝飾器標記控制器屬性方法等需要注入的引數,然後呼叫函式的時候從容器中取出(這裡碰到個坑),由於http
服務請求的上下文在回撥函式中,所以我繫結了一個回撥函式到容器中,需要獲取例項的時候將上下文傳入函式中,生成例如request
物件的例項。
提供者
這時候開發的框架,各種服務預設繫結在容器中,與應用類耦合,雖然是框架自帶的服務,但是還是不夠完美,所以借鑑了提供者的設計模式,將所有服務抽離並設計註冊服務的api,在框架啟動時,自動註冊預設服務。
這樣子,我們的所有服務與框架底層核心完全解耦,保證了底層核心的精簡,並具有強大的可伸縮性。
模組化
既然是一個web框架,使用的時候肯定會承載不同的業務,所以我們需要使用模組化功能拆分業務,提升可維護性,比如可以設定這個模組包含了哪幾個控制器(支援萬用字元),這個模組需要載入哪些中介軟體,甚至子模組功能
所以我就設計了這麼一套方案,使用模組描述類來定義模組
module.exports = class ExampleModule {
// 標示子模組
modules = [];
// 標示需要裝載的控制器
controllers = [];
// 標示需要載入的中介軟體
middlewares = [];
}複製程式碼
? good
請求
作為web框架,肯定需要解析請求啊什麼的,既然不是擴充套件 ctx
屬性,那麼我的方案就是使用 Request
類來解析 ctx
, 這樣的好處就是,我可以解析koa
的ctx
,express
的req
、 res
或者其他框架的上下文物件,並且這個類是註冊在容器中的,如果你有其他的解析方案,當然也可以自己註冊一個,然後為所欲為(沒錯,IOC
容器就是可以為所欲為?)。
響應
生產響應的時候肯定也要越方便越好,方便到你只需要在控制器中return
就好,可以return
各種型別,除了koa2
中的支援的資料型別,還支援直接返回 框架的 View
(檢視,即模板)例項或者Response
(響應類)例項等等,框架都會自動判斷。
class UserController {
index() {
return [{ id: 1 }]
}
}複製程式碼
就是這麼方便,當然不僅僅這樣,還有更多強大的功能。
驗證器
我在使用很多框架的時候,驗證請求資料不是很方便,所以也重新設計了一套方案(狗頭),
當然還是使用裝飾器模式
// 定義一個驗證類,放在指定目錄
class UserPostValidate extends Validate {
@MaxLength(10) username;
@Length(1, 20) password:
}複製程式碼
然後在控制器中
class UserController extends Controller {
store() {
this.request.validate('UserPostValidate')
}
}
複製程式碼
狗頭
寫了好久,先去吃飯了,其實還有很多功能模組,例如日誌服務、多程式服務、程式間通訊服務、安全相關服務、cookie
和session
服務等等,有興趣的可以留言繼續解答,或者出第二篇,下次出點技術類的~~~?
倉庫地址: [Github]
當然,框架核心程式碼在 framework
這個包,目前還在開發和測試中,優化一些功能和文件,工具比較low逼,也需要完善,希望有大牛可以一起開發。
目前已經到 0.8.x
版本,已經歷時半年(第一個npm包提交開始),離第一個穩定版不遠了!!!!正在快車道中,希望明年儘快開放 1.0
版本服務大眾。
也希望大家可以嘗試下提供各種意見反饋,由於目前就一個人開發,bug
肯定不少,cli
工具方面已經很久沒更新了,準備下一步完善工具,如果需要體驗的(工具萬一有bug
),直接clone daze
倉庫就可以。
然後,希望大家不要吝嗇自己的 star
????????給個鼓勵~~~~
最後,祝大家新年快樂,新年新氣象 ???????