Node.js Web 框架演进

1. Node.js http 模块

使用 Node.js http 模块来创建 Web 服务是最原始的方式。这种是最原始的处理方式,对理解原理非常有帮助。看下面代码

const http = require('http')
http.createServer( function(req, res) {
  console.log(req)
  
  if(req.url === '/'){
    req.end('Hello world')
  } else if (req.url === '/2') {
    req.end('Hello world2')
  } else {
    req.end('Hello world! other')
  }
} )

2 最早的 Connect

在上面的章节中,如果有成百上千个 URL,那么写代码将会变得很低效。值得注意的是,任何重复的工作都应该被抽象,Connect 就是这样子抽象出来的框架。下面是关于 Connect 的介绍。

Connect 是一个用 Node.js 编写的可扩展的 HTTP 服务器框架,因以中间件作为‘插件’而广为人知
Connect 的入门示例都很简单,如下:

const connect = require('connect')
const http = require('http')

const app = connect()
app.use(function(req,res,next) {
  res.end('Hello world')
})

http.createServer(app).listen(3000)

对比 1,2 小节的代码,可以看出如下几点:

  • 它们都是基于 http.createServer.
  • 它们的差异是,createServer 的参数是独立的,并且在 Connect 里通过 app.use 函数可以挂载多个插件式的中间件。
  • Connect 中提供了一个独立的 app 层。
  • Connect 支持中间件写法,可以通过 app.use 函数挂载中间件。

另外,值得注意的是编程时区分可变状态不可变状态,可变的抽取出去,不可变的固化下来。Connect 提供了可变部分的插件化,对于 Node.js Web 应用开发者来说,这是演进中必然的一部分。

Connect 提供了插件化的体系,方便我们去扩展和处理业务,下面将对 Connect 进行简单的介绍,以方便和 Express ,Koa 对比。

多URL 处理

const connect = require('connect')
const http = require('http')

const app = connect()

app.use('/2', function(req,res,next) {
  res.end('hello world')
})
app.use(function(req,res,next) {
  res.end('hello form connect')
})

http.createServer(app).listen(3000)

接下来访问 http://localhost:3000/2 就能看到 hello world。可以说明请求经过了第一个中间件。
上例说明,Connect 实际是将开发中的可变部分抽象出来,通过 app.use 函数挂载到 app 对象上的。这样子处理取来非常简单,清晰。

中间件的执行顺序

接下来测试下中间件执行顺序,将上面的代码中中间件的注册顺序改变下

app.use(function(req,res,next) {
  res.end('hello form connect')
})
app.use('/2', function(req,res,next) {
  res.end('hello world')
})

再次访问同样的地址时,将会得到 hello form connect,这与第一次得到的结果不同,这是因为第一个中间件没有指定请求路径,它会响应所有请求,正确的写法如下:

app.use('/',function(req,res,next) {
  res.end('hello form connect')
})
app.use('/2', function(req,res,next) {
  res.end('hello world')
})

这样子访问同样的路径,就能得到相同结果。这样子的写法其实和第一小节中,原始 http 模块编写的 if/else 是一样的。

  if(req.url === '/'){
    req.end('Hello world')
  } else if (req.url === '/2') {
    req.end('Hello world2')
  } else {
    req.end('Hello world! other')
  }

这样的写法会很烦人,必须考虑中间件的执行顺序让很多人埋怨 Node.js 中的 ‘坑’太多。但这是真的有问题,还是自己没能明白,很值得反思。


通过上面的代码对比,我们能得出如下的结论。

  • app.use 的中间件有顺序
  • 中间件可分类,全局的和局部的(路由里生效的)
  • 中间件的定义方法是 function(req,res){}

启动服务
Connect 模块本质上只是针对 http 模块的封装,提供了统一的中间件形式,这对解耦抽象是非常重要的。但是无论如何,Connect 的最终返回结果是 http.createServer 参数中的回调函数,故而启动过程和普通的 HTTP 服务器的自动过程是一样的。


接下来就是在 Connect 上出现的 Express 和后续出现的 Koa,它们有更加丰富的中间件,可以适应更多的场景。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容