node.js上除了Express還有哪些好用的web開發框架


 

老司機都有體會, 開發本身沒有多難, 最糾結其實是最初的技術和框架選型, 本沒有絕對的好壞之分, 可一旦選擇了不適合於自己業務場景的框架, 將來木已成舟后開發和維護成本都很高, 等發現不合適的時候更換的成本更是令人膽顫, 數據觀最早的接入層是采用ThinkPHP開發, 后來基於種種權衡后決定用node.js重制, web開發框架選型就成為首要必須慎重解決的問題, Express當然是頭號映入視野的名字, 本着全面考察重點擇優的原則又花不少時間簡單研究對比幾個主流的node.js web開發框架, 真的有亂花漸欲迷人眼的感覺, 把當初列入備選的幾個框架簡單評論一下:

  •Express 
  這個就無需再介紹了吧, 幾乎已經成為閉眼推薦的首位, 有點在於簡單靈活, 缺陷也在於過於簡單, 相當於每個功能都需要自己選擇不同組件搭建, 雖然有各自腳手架幫助, 但對於開發大一點的系統還是缺乏必要的代碼框架, 光搭建整合基礎框架就會花不少時間, 對於新上手node.js不久的筆者而言,自由靈活也意味着容易犯錯, 最好有類似Djongo/ThinkPHP那樣out-of-box即開即用的開發框架快速上手而不是坐而論道.
  優點: 插件眾多, 簡單, 自由, 豐儉由人, 適合於簡單業務邏輯模型 
  缺點: 缺少規范性, 需要自己選擇搭配的組件太多, 不太適合應用復雜的業務.

  • Koa 
  Express基於ES6的升級版, async/await解決ES5 callback hell的痼疾, 但是選擇框架不僅僅是框架本身, 同時還要看插件擴展的豐富和成熟度, 因為沒有用過擔心后面遇坑填不平而放棄, 做研究可以大膽, 做產品必須謹慎.
  優點: ES6語法, 邏輯易懂 
  缺點: 剛開始應用不久, 擔心有擴展不足和不兼容問題

  •Meteor 
  一個完全統一前后台開發的一站式框架, 從后台數據庫到前端view全部包含在內, 特別適合於重度依賴websocket的SPA(單頁面應用)開發, 國外流行的Asana就是完全采用Meteor框架開發.
  優點:一站式解決方案, 前后台一體開發, 強大的websocket+ mongoDB支持 
  缺點:自由度不夠, 和傳統的Web框架概念差異較大.

  •socketstream 
  如果說RESTful是Ajax的概念基礎, socketstream實際上更接近於早期RPC的思路, 將函數調用(functionname + parameters)建構在websocket協議層之上, 例如下面這段代碼其實就是調用遠程函數計算.

  //--server side in /server/rpc/app.js 
  exports.actions= function(req, res, ss){ 
  //return list of actions which can be called publicly 
  return{ 
  square:function(number){ 
  res(number* number); 
  } 
  } 
  } 
  //--client request 
  ss.rpc('app.square',25) 

  優點: 完全web socket, 函數+參數==>返回值概念簡單 
  缺點:和主流的RESTful概念偏離較大, 很少看到實際應用案例

  •Sails.js 
  這是本文的主角,相當於針對典型應用框架所需組件在Express基礎上的集成封裝,把日常開發常用的功能都給你集成好了, 開箱即用, 完全兼容Express的middleware, 如果了解ThinkPHP就更容易上手了, RoR / Convention overConfiguration的概念可以立即進入實際業務開發, 反正做什么事情應該怎么做人家都給你規定好了, 可以從實驗代碼逐步迭代到中大項目的生產代碼.
  除了傳統的HTTPRESTful外還同時支持websocket - 同一個請求協議既可以通過Ajax發送, 也可以通過websocket發送, 這一點讓人贊賞.
  優點: 開箱即用的全功能Express增強框架, 內置支持websocket 
  缺點:(據說)ORM性能不好

  Sails.js框架組成介紹

  既然是一個全功能的MVC框架, 那么開發一個應用所需的常用組件自然包含在內除非你有特殊要求, 這一點非常類似ThinkPHP, 從目錄分布上介紹一個概貌先:

  • api/ 
  Model /Controller都在這個目錄下, 所有代碼根據不同職責都被定義好位置, 這一點非常有利於大型項目開發的代碼規划.

  •api/controllers 
  這里面寫的每一個controller + function 都會被自動映射到路由上, 充分體現convention over configuration的特點, 與ThinkPHP如出一轍, 來一個最基礎的hello world吧 -api/controllers/TestController.hello() 方法被自動映射到/test/hello 而無需任何配置.

  //api/controllers/TestController.js 
  module.exports= { 
  hello:function (req, res) { 
  returnres.ok("Hello World"); 
  }, 
  controller下可以隨意進行目錄分層, 每增加一級目錄就相當增加一級路由.
  api/controllers/user/TestController.hello()==> /user/test/hello

  •api/services 
  項目大了程序就必須進行職能分層, 業務相關都作為獨立的service一層放在這個目錄下, controller僅負責request / response及差錯處理, 既簡化controller的代碼邏輯,又可以讓service代碼被不同模塊復用。作用相當於一套全局函數庫。
  常見錯誤:service代碼不要直接通過res做應答, 那本來是屬於controller的職責.

  •api/policies 
  作為前后台分離必然要提供Ajax API給前端, 那么訪問鑒權和安全控制必不可少, Sails.js在Express middleware基礎上封裝為policy實現訪問控制, 如果說上面那個 /test/hello 僅允許登錄用戶才能訪問, 那么加一個驗證session的policy就是.

  langset是檢測多語言設置的policy, sessionAuth僅允許帶有登錄session的API請求通過, 多個policy可以組成一個檢查鏈, 只有全通過了才能達到最終的controller, 中間任何一個policy都可以直接response拒絕服務.

  config/policies的鑒權配置
  module.exports.policies= { 
  '*':true, 
  TestController:{ 
  '*' :'langset', 
  'hello': ['langset', 'sessionAuth'], 
  }, 
  api/policies/sessionAuth.js實現

  如果是頁面請求則redirect, 如果是ajax則應答403forbidden. 
  這里還借用了passport的req.isAuthenticated(), 根據業務需要可以結合多種檢查方式, 把每個獨立檢查點都作為一個policy, 然后配置為一個policy chain, 功能獨立又可以靈活搭配, 類似應用登錄檢查中的異地登錄, 長時間未登錄, 連續密碼錯誤, 動態驗證碼校驗都可以用policy的思路去實現.

  module.exports= function (req, res, next) { 
  // Useris allowed, proceed to the next policy, 
  // orif this is the last policy, the controller 
  if(req.isAuthenticated()) { 
  returnnext(); 
  } 

  // Useris not allowed 
  //(default res.forbidden() behavior can be overridden in `config/403.js`) 
  if(req.wantsJSON) { 
  returnres.forbidden('You are not permitted to perform this action.'); 
  } 
  returnres.redirect("/login"); 
  }; 
圖片
  •api/responses 
  常用的response頁面都在這里了, 當然可以增加新的, 調用就是res.{response_name}(message)
  *badRequest.js - 400 
  *created.js - 201 
  *forbidden.js - 403 
  *notFound.js - 404 
  * ok.js- 200 
  *serverError.js - 500 
  Oops,404了

  •api/models 
  Sails.js的ORM實現, 這個目錄下的class都會映射為一個持久化的object及attributes, 采用Waterline hook機制實現, 可以配置為MySQL/MongoDB/Oracle/PostgreSQL多種常見數據庫.

  考慮到node.js更多作為中間轉接層角色出現,真正ORM與核心業務邏輯還是交由Java等后台實現為妥,所以個人並不建議過多采用Sails.js自身的ORM,聊勝於無吧。

  •assets/ 
  images/fonts/js/css/模板等靜態文件下載根路徑,build時可采用Grunt進行打包丑化等操作。

  •config/
  osession.js - 配置session可采用的存儲模式,redis/mongo等多種存儲方式可簡單配置支持。
  ohttp.js - default按照缺省配置就夠用了,如果還想自己微調可以完全配置Express的middleware次序。筆者在middleware里面增加了一個客制化的 response-time 中間件用於記錄每個API請求的log。
  olog.js - 配置log輸出appender,CAUTION:輸出log一定要用sails.log而不是console.log,因為可以根據需要配置log輸出方式而不修改代碼,筆者采用了sails-hook-winston+winston-daily-rotate-file實現按日期命名log文件。
  oroutes.js/policies.js - 路由和安全策略配置雙劍合璧,簡單而強大。
  oviews.js - 配置View模板庫。

  •views/ 
  如果是正式項目個人更建議用handlebars替換缺省的ejs模板庫,優雅替代模板里面的js代碼邏輯,尤其可以自定義helper函數瞬間讓開發者不必再依賴模板庫提供的有限邏輯處理,充分發揮函數式語言的優勢。

  這是原生ejs模板實現的403:

  <% if (typeof data!== 'undefined') { %> 
  <%= data %> 
  <% } else { %> 
  You don't have permission to see the page you'retrying to reach. 
  <% } %> 
  

  這是handlebars實現的403
  <divclass="content"> 
  {{#ifdata }} 
  {{data}} 
  {{else}} 
  您沒有權限查看您要訪問的網頁。 
  {{/if}} 
  </div> 

  Sails.js框架常用搭配的七種武器
  框架自身所能提供的功能畢竟是有限的,擴展庫才是一個框架豐滿與否的關鍵,此處結合筆者的實踐推薦幾個可用的框架擴展。

  1. 長生劍-async.js + lodash.js 
  這是兩個被內置的核心js庫,前者用於流程控制,后者用於數據結構處理,即使不用sails.js也應該深入掌握。

  2. 碧玉刀-handlebars 
  放棄ejs吧,handlebars更優雅簡潔,而且具有更強更容易的處理邏輯擴展能力。全部秘密就在於注冊一個自己的函數。
  這是一個實現編碼轉義的helper函數
  Handlebars.registerHelper('unescape',function (v) { 
  returnunescape(v); 
  }); 

  3. 離別鈎-winston 
  寫后台不充分考慮log記錄簡直等於不帶手機去旅行,sails-hook-winston可以無縫將sails.log轉接到任意winston transport插件上而無需修改任何代碼。
  console/ winston-daily-rotate-file / http / LogstashRedis 是實際使用中的transport。

  4. 霸王槍-passport 
  基於passport的第三方認證已經成為JS的標准,幾百個知名strategy即插即用,幾行配置代碼就可以實現微信/微博/釘釘/QQ第三方登錄認證,當然最好對OAuth2有簡單了解。

  5. 多情環-socket.io 
  Sails.js缺省是自帶websocket支持的,而且其model的CRUD操作也自動支持pub/sub操作,一個Ajax請求無論是走HTTP協議還是websocket協議在controller是同一個處理入口,header/session都是一樣的,這一特點真切讓人感到體貼,不必再針對不同功能采用不同協議然后生成不同代碼了。

  CAUTION:不能用於阿里雲防火牆后面的應用服務 
  實測下來socket會頻繁斷掉重連,而且每次都是不同的IP地址,懷疑兩點起因:
  • 阿里防火牆專門針對HTTP無狀態短連接設計,不允許出現長期的socket連接。
  • 為了負載分擔和惡意攻擊防護,不同請求入口IP地址會變化。

  6. 孔雀翎-pm2 
  如果說Java的代碼穩定是靠語法和Exception捕獲來保證的,那么node.js的邏輯反其道行之-如果錯了就盡快退出執行,反正catch回調函數拋出的異常就是水中月,進程生命周期管理最佳工具就是pm2,一行代碼都不用寫即可支持cluster運行模式,即使偶爾出錯導致進程退出也會自動重啟,確保服務不間斷。

  7. 拳頭-typescript 
  后台應用服務最關鍵的要求是什么?穩定,穩定,還是TMD的穩定。 
  面對一個過於自由沒有類型檢查的javascript語言,防范編碼錯誤的最佳武器就是套上類型語言的盔甲,class /interface / extends / enum etc,少量代碼時這是累贅,面對大量代碼和復雜結構與邏輯時有語言的支撐才是保命的秘訣。

更多web前端學習信息,加群434623999


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM