Node.js 的 Web 框架的 3 個(gè)層次,理清了就不迷茫
web 框架指的是處理 http、https 的服務(wù)端框架,Node.js 提供了 http、https 模塊用于處理協(xié)議數(shù)據(jù),這是 web 框架的基礎(chǔ)。
但是 http、https 的 api 過于簡單,用起來比較麻煩,所以一般會用 express、koa、fastify 這種封裝了一層的框架來簡化。
但 express 類的框架不提供代碼架構(gòu)方面的限制,所以對于模塊比較多比較復(fù)雜的企業(yè)級應(yīng)用來說并不適合,這時(shí)就要用實(shí)現(xiàn)了 MVC 的 eggjs、nestjs 這類企業(yè)級 web 框架。
這是 web 框架的 3 個(gè)層次,理清了它們的關(guān)系和適用場景,再去學(xué)習(xí)才不會迷茫。
下面我們分別來看一下:
http、https
http 是基于 TCP 的,對 TCP 傳過來的 http 協(xié)議數(shù)據(jù)做 parse,傳給 handler 處理,handler 處理完要返回 http 響應(yīng),這是 http 模塊做的事情。
- const http = require('http');
- const server = http.createServer((req, res) => {
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('okay');
- });
- server.listen(8080, '127.0.0.1');
http 模塊雖然能處理請求和響應(yīng),但是提供的 api 過于原始:
比如獲取請求參數(shù)還要用 url 模塊 parse 一次
- const http = require('http');
- const url = require('url');
- http.createServer(function (req, res) {
- const queryObject = url.parse(req.url,true).query;
- console.log(queryObject);
- res.writeHead(200, {'Content-Type': 'text/html'});
- res.end('xxx');
- }).listen(8080);
比如返回響應(yīng)只能用 write 或者 end 返回一段 buffer 或 string,想返回 JSON、文件下載、html 視圖等都要自己實(shí)現(xiàn)。
而且 get、post、put、delete 等請求類型也要自己做判斷。
- if(req.method === 'get') {
- //...
- } else if (req.method === 'post') {
- //...
- }
- //...
因?yàn)橛羞@些痛點(diǎn),所以一般我們不會直接用 http 模塊,而是用封裝了一層的 express、koa、fastify 這類 web 框架。
express、koa、fastify 等
express 這類框架解決了剛才的那個(gè)痛點(diǎn)問題:
- 提供了路由機(jī)制,不用自己手動判斷 method 和 path
- app.get('/list', function (req, res) {
- //...
- })
- app.post('/save', function(req, res) {
- //...
- })
- 提供了更好用的 request 和 response api:
比如 req.params 獲取請求參數(shù)
- app.get('/user/:id', function (req, res) {
- res.send('user ' + req.params.id)
- })
res.download 返回下載的響應(yīng)
- res.download('/report-12345.pdf')
res.render 返回模版引擎渲染的 html
- app.render('xxx-template', { name: 'guang' }, function (err, html) {
- // ...
- })
- 提供了中間件機(jī)制,用于復(fù)用一些一些邏輯:
比如文件上傳中間件
- app.use(fileUpload({
- useTempFiles : true,
- tempFileDir : '/tmp/'
- }));
提供了這么多方便的功能,確實(shí)比 http 模塊用起來簡單多了。
但是 express 類的 web 框架也有問題,就是沒有提供組織代碼的模式,當(dāng)模塊多了代碼很容易亂掉,因?yàn)樗皇前凑疹愃蒲笫[的順序調(diào)用中間件,沒有模塊和 MVC 的劃分。
express 類框架做一些小的服務(wù)可以,企業(yè)級應(yīng)用還得用 nestjs、eggjs 這類 MVC 框架。
nestjs、eggjs、midwayjs、daruk 等
nestjs 類的框架就實(shí)現(xiàn)了 MVC 的模式,代碼有明顯的 Controller、Service、Model、View 的劃分:
- import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
- import { CreateUserDto } from './dto/create-user.dto';
- import { User } from './user.entity';
- import { UsersService } from './users.service';
- @Controller('users')
- export class UsersController {
- constructor(private readonly usersService: UsersService) {}
- @Post()
- create(@Body() createUserDto: CreateUserDto): Promise<User> {
- return this.usersService.create(createUserDto);
- }
- @Get()
- findAll(): Promise<User[]> {
- return this.usersService.findAll();
- }
- @Get(':id')
- findOne(@Param('id') id: string): Promise<User> {
- return this.usersService.findOne(id);
- }
- @Delete(':id')
- remove(@Param('id') id: string): Promise<void> {
- return this.usersService.remove(id);
- }
- }
nestjs 是對標(biāo) java 的 spring 的,實(shí)現(xiàn)了 IOC、AOP 等模式,模塊之間耦合度很低,就算再復(fù)雜的項(xiàng)目,通過 Module、Controller、Service 等也可以很好的被組織起來,相比 express 來說,組織代碼方面提升了一個(gè)檔次。
nestjs 的底層就是 express、fastify 等 web 框架,而且還可以靈活的切換底層實(shí)現(xiàn)。
可以看到,nestjs、eggjs 類的企業(yè)級框架,除了有豐富的 api 以外,更重要的是提供了代碼組織的規(guī)范,通過 Module、Controller、Service 等概念可以很好的組織復(fù)雜的業(yè)務(wù)邏輯。
總結(jié)
web 框架都是基于 http、https 模塊,但它提供的 api 過于原始,使用起來比較麻煩,所以我們一般會用 express、koa 這類框架來簡化,它提供了中間件機(jī)制來復(fù)用邏輯,提供了更多的 request、response 的 api,但卻沒有組織復(fù)雜代碼的能力,對于企業(yè)級的復(fù)雜應(yīng)用,還是會用 nestjs、eggjs 這類 MVC 框架,它們的底層是 express、koa,但提供了 Module、Controller、Service 等概念,可以很好的組織復(fù)雜的代碼。
要理清楚為什么會有這三個(gè)層次,都各自適合什么場景,這樣才能更好的掌握它們,在技術(shù)選型上才不會迷茫。