自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用Node.js實(shí)現(xiàn)一個(gè)express框架

開(kāi)發(fā) 前端
本文介紹了如何使用Node.js來(lái)實(shí)現(xiàn)一個(gè)express框架的,一起來(lái)看看吧。

 [[336666]]

手寫一個(gè)express系列

express的基本用法 

  1. const express = require("express");  
  2. const app = express();  
  3. app.get("/test", (req, res, next) => {  
  4.   console.log("*1");  
  5. //   res.end("2");  
  6.   next();  
  7. });  
  8. app.get("/test", (req, res, next) => {  
  9.   console.log("*2");  
  10.   res.end("2");  
  11. });  
  12. app.listen(8888, (err) => {  
  13.   !err && console.log("監(jiān)聽(tīng)成功");  
  14. }); 
  •  當(dāng)我訪問(wèn)localhost:8888/test時(shí)候,返回了:2,服務(wù)端打印了 
  1. *1  
  2. *2 
  •  從上面可以看到什么?
    •  express默認(rèn)引入調(diào)用后返回一個(gè)app對(duì)象
    •  app.listen 會(huì)啟動(dòng)進(jìn)程監(jiān)聽(tīng)端口
    •  每次收到請(qǐng)求,對(duì)應(yīng)的url和method會(huì)觸發(fā)相應(yīng)掛載在app上對(duì)應(yīng)的回調(diào)函數(shù)
    •  調(diào)用 next 方法,會(huì)觸發(fā)下一個(gè)

一起來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的express框架

  •  定義屬于我們的express文件入口,這里使用class來(lái)實(shí)現(xiàn) 
  1. class express {  
  2.  
  3. module.exports = express
  •  需要的原生模塊http,創(chuàng)建進(jìn)程監(jiān)聽(tīng)端口 
  1. const { createServer } = require("http"); 
  •  給 class 定義 listen 方法,監(jiān)聽(tīng)端口 
  1. class express {  
  2.   listen(...args) {  
  3.     createServer(cb).listen(...args);  
  4.   }  
  •  這樣就可以通過(guò)調(diào)用 class 的 listen 去調(diào)用 http 模塊的 listen 了,這里的cb我們可以先不管,你要知道每次接受到請(qǐng)求,必然會(huì)調(diào)用 cb 函數(shù),這個(gè)是 createServer 原生模塊幫我們封裝好的

實(shí)現(xiàn)接收到請(qǐng)求觸發(fā)

  •  實(shí)現(xiàn)app.get app.post等方法
    •  目前我們接受到響應(yīng),就會(huì)觸發(fā) cb 這個(gè)回調(diào)函數(shù),那我們打印下,看看是什么參數(shù)? 
  1. class express {  
  2.   cb() {  
  3.     return (req, res) => {  
  4.       console.log(res, res, "開(kāi)始行動(dòng)");  
  5.     };  
  6.   }  
  7.   listen(...args) {  
  8.     createServer(this.cb()).listen(...args);  
  9.   }  
  •  發(fā)現(xiàn)此時(shí)的 req 和 res 正是我們想要的可讀流和可寫流.
  •  開(kāi)始編寫 get 和 post 方法
    •  這里注意,有路由是'/'的,這種是不管任何路由都會(huì)觸發(fā)一次 
  1. constructor() {  
  2.     this.routers = {  
  3.       get: [],  
  4.       post: [],  
  5.     };  
  6.   }  
  7.   get(path, handle) {  
  8.     this.routers.get.push({  
  9.       path,  
  10.       handle,  
  11.     });  
  12.   }  
  13.   post(path, handle) {  
  14.     this.routers.post.push({  
  15.       path,  
  16.       handle,  
  17.     });  
  18.   } 
  •  初始化時(shí)候定義 get、post 的數(shù)組儲(chǔ)存對(duì)應(yīng)的 path 和 handle.
  •  需要觸發(fā)路由回調(diào)的時(shí)候,首先要找到對(duì)應(yīng)的請(qǐng)求方式下對(duì)應(yīng)的 url 的 handle 方法,然后觸發(fā)回調(diào).
  •  如何找到對(duì)應(yīng)請(qǐng)求方式下的 url 對(duì)應(yīng)的 handle 方法? 在接到請(qǐng)求時(shí)候就要遍歷一次
    •  這里要考慮匹配多個(gè)路由,意味著,我們可能遇到像最開(kāi)始一樣,有兩個(gè) get 方式的 test 路由 
  1. cb() {  
  2.   return (req, res) => {  
  3.     const method = req.method.toLowerCase();  
  4.     console.log(this.routers[method], ",method");  
  5.     const url = req.url;  
  6.     this.routers[method].forEach((item) => {  
  7.       item.path === url && item.handle(req, res);  
  8.     });  
  9.   };  
  10.  
  11. listen(...args) {  
  12.   createServer(this.cb()).listen(...args);  
  •  上面根據(jù) method 找到對(duì)應(yīng)的數(shù)組,遍歷找到請(qǐng)求的路由,觸發(fā)回調(diào),此時(shí)已經(jīng)能正常返回?cái)?shù)據(jù)了 
  1. [ { method: 'get', path: '/test', handle: [Function] } ] ,method 
  •  此時(shí)最簡(jiǎn)單的express已經(jīng)完成了,但是我們好像忘了最重要的中間件

完成最重要的中間件功能

  •  首先要知道,express中間件分兩種,一種帶路由的,那就是根據(jù)路由決定是否觸發(fā)
  •  另外一種就是不帶路由的,像靜態(tài)資源這種. 是用戶訪問(wèn)任何路由都要觸發(fā)一次的
  •  那我們需要一個(gè) all 數(shù)組儲(chǔ)存這種任意路由都需要匹配觸發(fā)的 
  1. constructor() {  
  2.    this.routers = {  
  3.      get: [],  
  4.      post: [],  
  5.      all: [],  
  6.    };  
  7.  } 
  •  之前的直接通過(guò) push 方式是太粗暴.如果用戶需要中間件功能,不傳路由,那就要做特殊處理,這里通過(guò)一個(gè)中間函數(shù)處理下
  •  改造get、post方法,定義handleAddRouter方法 
  1. handleAddRouter(path, handle) {  
  2.    let router = {}; 
  3.     if (typeof path === "string") {  
  4.      router = {  
  5.        path,  
  6.        handle,  
  7.      };  
  8.    } else {  
  9.      router = {  
  10.        path: "/",  
  11.        handle: path, 
  12.      };  
  13.    }  
  14.    return router;  
  15.  }  
  16.  get(path, handle) {  
  17.    const router = this.handleAddRouter(path, handle);  
  18.    this.routers.get.push(router);  
  19.  }  
  20.  post(path, handle) {  
  21.    const router = this.handleAddRouter(path, handle);  
  22.    this.routers.post.push(router); 
  23.   }  
  24.  use(path, handle) {  
  25.    const router = this.handleAddRouter(path, handle);  
  26.    this.routers.all.push(router);  
  27.  } 
  •  每次添加之前,先觸發(fā)一次handleAddRouter,如果是 path 為空的中間件,直接傳入函數(shù)的,那么 path 幫它設(shè)置成'/'
  •  我們還遺留了一個(gè)點(diǎn),next的實(shí)現(xiàn),因?yàn)槲覀儸F(xiàn)在加了all這個(gè)數(shù)組后,意味著可能有多個(gè)中間件,那么可能一次請(qǐng)求打過(guò)來(lái),就要觸發(fā)多個(gè)路由

這里要注意,promise.then 源碼實(shí)現(xiàn)和 express 的 next、以及 koa 的洋蔥圈、redux 的中間件實(shí)現(xiàn),有著一丁點(diǎn)相似,當(dāng)你能真的領(lǐng)悟前后端框架源碼時(shí)候,發(fā)現(xiàn)大都相似

  •  閱讀我的文章,足以擊破所有前后端源碼.而且可以手寫出來(lái), 我們只學(xué)最核心的,抓重點(diǎn)學(xué)習(xí),野蠻生長(zhǎng)!

實(shí)現(xiàn)next

  •  思路:
    •  首先要找到所有匹配的路由
    •  然后逐個(gè)執(zhí)行(看 next 的調(diào)用)
  •  定義search方法,找到所有匹配的路由 
  1. search(method, url) {  
  2.     const matchedList = [];  
  3.     [...this.routers[method], ...this.routers.all].forEach((item) => {  
  4.       item.path === url && matchedList.push(item.handle);  
  5.     });  
  6.     return matchedList;  
  7.   }  
  8.   cb() {  
  9.     return (req, res) => {  
  10.       const method = req.method.toLowerCase();  
  11.       const url = req.url;  
  12.       const matchedList = this.search(method, url);  
  13.     };  
  14.   } 
  •  matchedList就是我們想要找到的所有路由
  •  為了完成next,我們要將req ,res , matchedList存入閉包中,定義handle方法 
  1. handle(req, res, matchedList) {  
  2.    const next = () => {  
  3.      const midlleware = matchedList.shift();  
  4.      if (midlleware) {  
  5.        midlleware(req, res, next);  
  6.      }  
  7.    };  
  8.    next();  
  9.  }  
  10.  cb() {  
  11.    return (req, res) => {  
  12.      const method = req.method.toLowerCase();  
  13.      const url = req.url;  
  14.      const matchedList = this.search(method, url);  
  15.      this.handle(req, res, matchedList);  
  16.    };  
  17.  } 
  •  這樣我們就完成了next方法,只要手動(dòng)調(diào)用 next 就會(huì)調(diào)用下一個(gè)匹配到的路由回調(diào)函數(shù)
  •  不到一百行代碼,就完成了這個(gè)簡(jiǎn)單的express框架

寫在最后

  •  只要你根據(jù)我這些文章去認(rèn)真自己實(shí)現(xiàn)一次,一年內(nèi)拿個(gè) P6 應(yīng)該沒(méi)什么問(wèn)題
  •  大道至簡(jiǎn),希望你能通過(guò)這些文章真的學(xué)到框架的原理,進(jìn)而自己能寫出一些框架,走向更高的層級(jí)
  •  我是Peter,曾經(jīng) 20 萬(wàn)人超級(jí)群桌面軟件的架構(gòu)師,現(xiàn)在就職于明源云,擔(dān)任分公司前端負(fù)責(zé)人,目前深圳這邊需要招聘兩位中高級(jí)前端,3D數(shù)據(jù)可視化方向,期待你的到來(lái)
  •  如果感覺(jué)本文對(duì)你有幫助,別忘了點(diǎn)個(gè)在看和關(guān)注. 我們的技術(shù)團(tuán)隊(duì)也會(huì)不斷產(chǎn)出原創(chuàng)文章, 一起見(jiàn)證各位的成長(zhǎng) 

 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2011-10-25 09:28:30

Node.js

2022-10-18 18:43:40

Node.js低代碼

2017-04-24 08:31:26

Node.jsExpress.jsHTTP

2019-04-15 11:00:46

框架Node.JS開(kāi)發(fā)

2020-05-29 15:33:28

Node.js框架JavaScript

2023-05-12 07:31:58

NuxtVue.js

2020-07-15 08:06:04

Node.js框架開(kāi)發(fā)

2021-07-16 04:56:03

NodejsAddon

2014-07-11 14:16:15

AbsurdJSExpress

2021-08-20 16:05:28

JavaScript node.js 應(yīng)用安全

2020-10-29 16:00:03

Node.jsweb前端

2019-08-29 10:58:02

Web 開(kāi)發(fā)框架

2022-05-23 10:26:50

Node.jsJavaScrip

2011-06-17 10:29:04

Nodejavascript

2022-06-05 13:52:32

Node.jsDNS 的原理DNS 服務(wù)器

2014-04-10 09:43:00

Node.jsTwilio

2020-02-25 12:27:59

Node.jsWeb開(kāi)發(fā)前端

2012-01-10 10:04:43

Node.js

2022-08-28 16:30:34

Node.jsDocker指令

2012-03-07 14:32:41

Node.js
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)