老司机都有体会, 开发本身没有多难, 最纠结其实是最初的技术和框架选型, 本没有绝对的好坏之分, 可一旦选择了不适合于自己业务场景的框架, 将来木已成舟后开发和维护成本都很高, 等发现不合适的时候更换的成本更是令人胆颤, 数据观最早的接入层是采用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的思路, 将函数调用(function name + 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', )

优点: 完全web socket, 函数+参数==>返回值概念简单

缺点:和主流的RESTful概念偏离较大, 很少看到实际应用案例

  • Sails.js

    这是本文的主角, 相当于针对典型应用框架所需组件在Express基础上的集成封装, 把日常开发常用的功能都给你集成好了, 开箱即用,
    完全兼容Express的middleware, 如果了解ThinkPHP就更容易上手了, RoR / Convention over
    Configuration的概念可以立即进入实际业务开发, 反正做什么事情应该怎么做人家都给你规定好了,
    可以从实验代码逐步迭代到中大项目的生产代码.

除了传统的HTTP RESTful外还同时支持websocket - 同一个请求协议既可以通过Ajax发送, 也可以通过websocket发送, 这一点让人赞赏.
优点: 开箱即用的全功能Express增强框架, 内置支持websocket

缺点:(据说)ORM性能不好

 

经过半个月的技术调研和评估后最终决定将原来基于ThinkPHP开发的接入控制层全部移植到Sails.js框架上,
虽然难免遇到技术问题需要解决, 但整体而言进展顺利, API访问延迟从100ms降低到10几ms,主要是从此有了一个Service
Runtime解决PHP无状态而强依赖Redis的问题.
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) {
return res.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则应答403 forbidden.

这里还借用了passport的req.isAuthenticated(), 根据业务需要可以结合多种检查方式,
把每个独立检查点都作为一个policy, 然后配置为一个policy chain, 功能独立又可以灵活搭配, 类似应用登录检查中的异地登录,
长时间未登录, 连续密码错误, 动态验证码校验都可以用policy的思路去实现.


module.exports = function (req, res, next) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
if (req.isAuthenticated()) {
return next();
}
 
// User is not allowed
// (default res.forbidden() behavior can be overridden in `config/403.js`)
if (req.wantsJSON) {
return res.forbidden('You are not permitted to perform this action.');
}
return res.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/
    • session.js - 配置session可采用的存储模式,redis/mongo等多种存储方式可简单配置支持。
    • http.js - default按照缺省配置就够用了,如果还想自己微调可以完全配置Express的middleware次序。笔者在middleware里面增加了一个客制化的 response-time 中间件用于记录每个API请求的log。
    • log.js
      -
      配置log输出appender,CAUTION:输出log一定要用sails.log而不是console.log,因为可以根据需要配置log输出方式而不修改代码,笔者采用了sails-hook-winston+winston-daily-rotate-file实现按日期命名log文件。
    • routes.js/policies.js - 路由和安全策略配置双剑合璧,简单而强大。
    • views.js - 配置View模板库。
  • views/

    如果是正式项目个人更建议用handlebars替换缺省的ejs模板库,优雅替代模板里面的js代码逻辑,尤其可以自定义helper函数瞬间让开发者不必再依赖模板库提供的有限逻辑处理,充分发挥函数式语言的优势。

这是原生ejs模板实现的403:

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

这是handlebars实现的403

<div class="content">
{{#if data }}
{{data}}
{{else}}
您没有权限查看您要访问的网页。
{{/if}}
</div>

Sails.js框架常用搭配的七种武器
框架自身所能提供的功能毕竟是有限的,扩展库才是一个框架丰满与否的关键,此处结合笔者的实践推荐几个可用的框架扩展。
1. 长生剑-async.js + lodash.js

这是两个被内置的核心js库,前者用于流程控制,后者用于数据结构处理,即使不用sails.js也应该深入掌握。

2. 碧玉刀-handlebars

放弃ejs吧,handlebars更优雅简洁,而且具有更强更容易的处理逻辑扩展能力。全部秘密就在于注册一个自己的函数。

这是一个实现编码转义的helper函数

Handlebars.registerHelper('unescape', function (v) {
return unescape(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开发者有些许参考,其实更可悲的是现今Web开发并没有能一统江山的框架(而且短期还真看不到这个希望),随着时间流逝和技术的飞速发展,恁多风流难免被雨打风吹去,枉回首,如落红湮灭。

node.js上除了Express还有哪些好用的web开发框架的更多相关文章

  1. node.js 下依赖Express 实现post 4种方式提交参数

    上面这个图好有意思啊,哈哈, v8威武啊.... 在2014年的最后一天和大家分享关于node.js 如何提交4种格式的post数据. 上上一篇说到了关于http协议里定义的4种常见数据的post方法 ...

  2. Node.js的框架-express

    Node.js的框架 express 是第三方的 express const express=require('express'); const app=express(); const PORT=3 ...

  3. Node.js 实战 & 最佳 Express 项目架构

    Node.js 实战 & 最佳 Express 项目架构 Express Koa refs https://github.com/xgqfrms/learn-node.js-by-practi ...

  4. node.js入门及express.js框架

    node.js介绍 javascript原本只是用来处理前端,Node使得javascript编写服务端程序成为可能.于是前端开发者也可以借此轻松进入后端开发领域.Node是基于Google的V8引擎 ...

  5. 建立一个node.js服务器(使用express搭建第一个Web环境)

    一.官网下载node.js 下载地址:https://nodejs.org/en/download/ 根据向导,下一步安装就可以了! 二.使用express搭建Web环境 express是一个开源的n ...

  6. Node.js 入门:Express + Mongoose 基础使用

    前言 Express 是基于 Node.js 平台的 web 应用开发框架,在学习了 Node.js 的基础知识后,可以使用 Express 框架来搭建一个 web 应用,实现对数据库的增删查改. 数 ...

  7. nodejs实战《一起学 Node.js》 使用 Express + MongoDB 搭建多人博客

    GitHub: https://github.com/nswbmw/N-blog N-blog 使用 Express + MongoDB 搭建多人博客 开发环境 Node.js: 6.9.1 Mong ...

  8. 深入浅出Node.js(上)

    (一):什么是Node.js Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟 ...

  9. node.js 基础学习 express安装使用

    安装好nodeJs,我们需要使用命令行中安装express. 我这里默认将Node.js安装在C:\Program Files\nodCejs\盘中. 在保持联网的状态下,依次输入如下命令. npm ...

随机推荐

  1. Weave 如何与外网通信?- 每天5分钟玩转 Docker 容器技术(66)

    上一节我们学习了 Weave 网络内部如何通信,今天讨论 Weave 如何与外界通信. weave 是一个私有的 VxLAN 网络,默认与外部网络隔离.外部网络如何才能访问到 weave 中的容器呢? ...

  2. Java 定时器 Timer 的使用.

    一.概念       定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和多线程技术还是有非常大的关联的.在JDK中Timer类主要负责计划任务的功能, ...

  3. python 斐波那契数列 fibonacci

    在python中生成fibonacci数列的函数 def fibonacci(): list = [] while 1: if(len(list) < 2): list.append(1) el ...

  4. 查看numpy.ndarray的数据类型

    使用ndarray数据时,如果希望知道数据的类型和维数,可以按照以下方法: Xxx.dtype  #xxx表示一个ndarray类型的变量,返回ndarray的数据类型 Xxx.shape  #xxx ...

  5. 最简单实用的JQuery实现banner图中的text打字动画效果!!!

    下面,就让小博详细介绍如何实现上面GIF实现的banner图中的文字动画效果,最简单实用的方法(鉴于代码量较小,就内嵌在一个HTML文件中了): 首先,我们要在header导入一个jQuery,并新建 ...

  6. 团队作业4——第一次项目冲刺(Alpha版本) 4.24

    团队作业4--第一次项目冲刺(Alpha版本) Day four: 会议照片 每日站立会议: 项目进展 今天是项目的Alpha敏捷冲刺的第三天,先大概整理下昨天已完成的任务以及今天计划完成的任务.今天 ...

  7. 【Alpha】Daily Scrum Meeting——Day1

    站立式会议照片 每个人的工作分配 成 员 今日计划完成的任务 胡丹丹 学习手机app开发的总体流程以及搭建APP开发所需要的安卓环境,加强Java语言的学习掌握 曾丽君 学习手机app开发的总体流程以 ...

  8. 201521123037 《Java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 查看ArrayLi ...

  9. 201521123104《java程序设计》第13周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  10. Activiti第一篇【介绍、配置开发环境、快速入门】

    Activiti介绍 什么是Activiti? Activiti5是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理.工作流.服务协作等领域的一个开 ...