逐行解析Express核心原理
文章主要以一下三個部分組成
- node 創(chuàng)建http服務
- express 創(chuàng)建http服務
- 自己寫類express并且解析核心原理
1. node創(chuàng)建http 服務
基于node基本的創(chuàng)建服務
//引入node 模塊
const http = require('http')
//創(chuàng)建服務蚕苇,并且實現(xiàn)callback回調(diào)
const server = http.createServer(callback)
const callback = (req,res) =>{
res.end("Hello xie")
}
//服務監(jiān)聽3000 端口
server.listen(3000)
上面我們用最原始的方式 創(chuàng)建一個服務哩掺,當訪問localhost:3000即可返回相關數(shù)據(jù)
2. 使用express 創(chuàng)建服務
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ù)function
/**
定義一個中間件,
暫且忽略next是啥嚼吞,知道next是一個往下傳遞的方法即可
*/
const middle = (req,res,next) =>{
//do something
next()
}
app.use(middle)
app.use("/api",middle, (req,res,next)=>{
//dosomething
next()
})
app.get(參數(shù)類似use參數(shù))
app.post(參數(shù)類似use參數(shù)){
res.json({
name:'xieyusheng'
})
}
......
那么問題來了盒件,app.use/get/post/res.json以及最至關重要的next是怎么實現(xiàn)的呢?
3.自己寫個Express框架
姑且就叫XExpress
1. 先定義接結構
const http = require('http')
class XExpress{
use () {
}
get () {
}
post () {
}
callback (){
}
listen(...argus){
const server = http.createServer(callback)
server.listen(...argus)
}
}
module.exports = () => new Exprerss()
實例化
const xErpress = './xExpress'
const server = new xErpress()
server.listen(3000)
ok舱禽,基本服務啟動好了炒刁,那么use/get等怎么處理呢,這里核心原理來了哦誊稚,其實我們只要將所有的中間件函數(shù)放在一個棧中翔始,然后一個一個執(zhí)行處理,那么怎么一個個處理呢里伯? 使用next函數(shù)
2. 將所有的中間件放在堆棧中
//new XExpress 實例化一個單量
constructor(){
//存放中間件的list
this.routes = {
use: [], // app.use(...)
get: [], // app.get(...)
post: [] // app.post(...)
}
}
//結構化傳遞來的參數(shù)
register (path) {
const info = {};
if(typeof path == "string"){
info.path = path;
//將后面的方法以arr的方式城瞎,放入內(nèi)存中,從第二個參數(shù)開始
info.stack = slice.call(arguments, 1)
}else{
info.path = "/" //當use等方法的第一個參數(shù)是否是鏈接
//從第一個參數(shù)開始疾瓮,轉換為數(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類似
所有的中間件都賦予在routes里面,那么當請求來了狼电,我們匹配下认然,然后一個個的執(zhí)行
callback(req,res) => {
//定義方法給req
req.json= () =>{
// 定義返回格式
res.setHeader("Contype-type" : "application/json")
res.end(
JSON.stringify(data)
)
}
//1.匹配相對的中間件
const url = req.url
const method = req.method.toLowerCase()
//匹配
const resultList = this.match(method, url)
//一個一個的執(zhí)行中間件函數(shù)
this.actionNext(req, res, resultList)
}
/**
根據(jù)url和method匹配相對應的中間件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() //取出第一個
if(middleware) {
//將next 函數(shù)傳遞
middleware(req,res,next)
}
}
next();
}
這樣,next函數(shù)傳遞給每個中間函數(shù)了漫萄,這里的next函數(shù)就是下一個要執(zhí)行函數(shù)的包裝體