逐行解析Express核心原理
文章主要以一下三個(gè)部分組成
- node 創(chuàng)建http服務(wù)
- express 創(chuàng)建http服務(wù)
- 自己寫(xiě)類(lèi)express并且解析核心原理
1. node創(chuàng)建http 服務(wù)
基于node基本的創(chuàng)建服務(wù)
//引入node 模塊
const http = require('http')
//創(chuàng)建服務(wù),并且實(shí)現(xiàn)callback回調(diào)
const server = http.createServer(callback)
const callback = (req,res) =>{
res.end("Hello xie")
}
//服務(wù)監(jiān)聽(tīng)3000 端口
server.listen(3000)
上面我們用最原始的方式 創(chuàng)建一個(gè)服務(wù),當(dāng)訪問(wèn)localhost:3000即可返回相關(guān)數(shù)據(jù)
2. 使用express 創(chuàng)建服務(wù)
npm install express --save
const express = require('express')
const app = express()
const port = 3000
//路由
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
使用express的優(yōu)勢(shì)在于其可以使用中間件,這也是其核心,所謂的中間件,其實(shí)也即使一個(gè)函數(shù)function
/**
定義一個(gè)中間件,
暫且忽略next是啥,知道next是一個(gè)往下傳遞的方法即可
*/
const middle = (req,res,next) =>{
//do something
next()
}
app.use(middle)
app.use("/api",middle, (req,res,next)=>{
//dosomething
next()
})
app.get(參數(shù)類(lèi)似use參數(shù))
app.post(參數(shù)類(lèi)似use參數(shù)){
res.json({
name:'xieyusheng'
})
}
......
那么問(wèn)題來(lái)了,app.use/get/post/res.json以及最至關(guān)重要的next是怎么實(shí)現(xiàn)的呢?
3.自己寫(xiě)個(gè)Express框架
姑且就叫XExpress
1. 先定義接結(jié)構(gòu)
const http = require('http')
class XExpress{
use () {
}
get () {
}
post () {
}
callback (){
}
listen(...argus){
const server = http.createServer(callback)
server.listen(...argus)
}
}
module.exports = () => new Exprerss()
實(shí)例化
const xErpress = './xExpress'
const server = new xErpress()
server.listen(3000)
ok,基本服務(wù)啟動(dòng)好了,那么use/get等怎么處理呢,這里核心原理來(lái)了哦,其實(shí)我們只要將所有的中間件函數(shù)放在一個(gè)棧中,然后一個(gè)一個(gè)執(zhí)行處理,那么怎么一個(gè)個(gè)處理呢? 使用next函數(shù)
2. 將所有的中間件放在堆棧中
//new XExpress 實(shí)例化一個(gè)單量
constructor(){
//存放中間件的list
this.routes = {
use: [], // app.use(...)
get: [], // app.get(...)
post: [] // app.post(...)
}
}
//結(jié)構(gòu)化傳遞來(lái)的參數(shù)
register (path) {
const info = {};
if(typeof path == "string"){
info.path = path;
//將后面的方法以arr的方式,放入內(nèi)存中,從第二個(gè)參數(shù)開(kāi)始
info.stack = slice.call(arguments, 1)
}else{
info.path = "/" //當(dāng)use等方法的第一個(gè)參數(shù)是否是鏈接
//從第一個(gè)參數(shù)開(kāi)始,轉(zhuǎn)換為數(shù)組,存入 stack
info.stack = slice.call(arguments, 1)
}
return info;
// info={
// path:'/',
// stack:[
// ()=>{};
// ()=>{}
// ]
// }
}
use(){
const info = this.register.apply(this, arguments)
this.routes.all.push(info)
}
....get/post類(lèi)似
所有的中間件都賦予在routes里面,那么當(dāng)請(qǐng)求來(lái)了,我們匹配下,然后一個(gè)個(gè)的執(zhí)行
callback(req,res) => {
//定義方法給req
req.json= () =>{
// 定義返回格式
res.setHeader("Contype-type" : "application/json")
res.end(
JSON.stringify(data)
)
}
//1.匹配相對(duì)的中間件
const url = req.url
const method = req.method.toLowerCase()
//匹配
const resultList = this.match(method, url)
//一個(gè)一個(gè)的執(zhí)行中間件函數(shù)
this.actionNext(req, res, resultList)
}
/**
根據(jù)url和method匹配相對(duì)應(yīng)的中間件LIST
*/
match(method, url) {
let stack = []
// 獲取 routes
let curRoutes = []
curRoutes =[...this.routes.use]
curRoutes = [...this.routes.use[method]]
curRoutes.forEach(routeInfo => {
if (url.indexOf(routeInfo.path) === 0) {
stack = stack.concat(routeInfo.stack)
}
})
return stack
}
actionNext(req, res, stack){
// 定義next函數(shù)
const next = () =>{
const middleware = stack.shift() //取出第一個(gè)
if(middleware) {
//將next 函數(shù)傳遞
middleware(req,res,next)
}
}
next();
}
這樣,next函數(shù)傳遞給每個(gè)中間函數(shù)了,這里的next函數(shù)就是下一個(gè)要執(zhí)行函數(shù)的包裝體