摘要:上面代碼的關(guān)鍵是模塊的方法,表示生成一個服務(wù)器實(shí)例。該方法接受一個回調(diào)函數(shù),該回調(diào)函數(shù)的參數(shù),分別為代表請求和回應(yīng)的對象和對象。循環(huán)請求過來時放入數(shù)組的對象,當(dāng)請求方法和路徑與對象中的一致時,執(zhí)行回調(diào)方法。
目錄
概述
hello-world 實(shí)例
運(yùn)行原理
多路由多回調(diào)以及中間件
概述Express是一個基于 Node.js 平臺,快速、開放、極簡的 web 開發(fā)框架。主要有 路由、中間件、模板引擎、 錯誤處理等功能
Hello world 實(shí)例在test文件夾中新加1.helloworld.js
var express = require("express"); var app = express(); app.get("/", function (req, res) { res.end("Hello World!"); }); var server = app.listen(3000, function () { console.log("Example app listening at 3000"); });
運(yùn)行 1.helloworls.js
node 1.helloworls.js
上面代碼會在本機(jī)的3000端口啟動一個網(wǎng)站,網(wǎng)頁顯示Hello World。
運(yùn)行原理現(xiàn)在新建lib文件夾我們手寫一個自己的express庫 了解其運(yùn)行原理
YUAN-EXPRESS | | | - lib | | - application.js #包裹app層 | | - express.js #框架入口 | | - test | | - 1.helloworld.js |
express.js
const Application = require("./application"); function createApplicaton() { return new Application(); } module.exports = createApplicaton;
目的:在application.js中實(shí)現(xiàn)實(shí)例中app.get、app.listen兩個方法
操作:構(gòu)造Appliaction函數(shù),在原型上添加 get、listen方法
application.js
const http = require("http") const url = require("url") let router = [{ path:"*", method:"*", handler(req,res){ res.end(`Cannot ${req.method}_${req.url}`) } }] function Application() { } Application.prototype.get = function (path,handler) {//在Application原型上添加get方法 router.push({ path, method: "get", handler }) } Application.prototype.listen = function () {//在Application原型上添加listen方法匹配路徑,執(zhí)行對應(yīng)的handler方法 let self = this const server = http.createServer(function (req,res) { let { pathname } = url.parse(req.url,true) for(var i = 1;iExpress框架建立在node.js內(nèi)置的http模塊上。
上面代碼的關(guān)鍵是http模塊的createServer方法,表示生成一個HTTP服務(wù)器實(shí)例。該方法接受一個回調(diào)函數(shù),該回調(diào)函數(shù)的參數(shù),分別為代表HTTP請求和HTTP回應(yīng)的request對象和response對象。
循環(huán)請求過來時放入router數(shù)組的對象,當(dāng)請求方法和路徑與對象中的一致時,執(zhí)行回調(diào)handler方法。
多路由多回調(diào)以及中間件測試用例
const express = require("../lib/express"); const app = express(); /** * 1.get是指定多個處理函數(shù) * 2.中間件錯誤處理 * 3. 子路徑系統(tǒng) 多帶帶創(chuàng)建一個子路徑系統(tǒng),并且把它掛載到主路徑 系統(tǒng)上 * */ /** * app.use * express.Router(); */ app.use(function (req, res, next) { console.log("Ware1:", Date.now()); next(); }); //路由是完整匹配的。/ != /user 所以進(jìn)不來 app.get("/", function (req, res, next) { res.end("1"); }); //創(chuàng)建一個新的路由容器,或者說路由系統(tǒng) const user = express.Router();// router user.use(function (req, res, next) { console.log("Ware2", Date.now()); next(); }); //在子路徑里的路徑是相對于父路徑 user.get("/2", function (req, res, next) { res.end("2"); }); //use表示使用中間件,只需要匹配前綴就可以了 app.use("/user", user);//user第二個參數(shù)是處理函數(shù) (req,res,next) // req.url = /user/3 //app.use("/user", artcile); app.use(function (err, req, res, next) { res.end("catch " + err); }); app.listen(3000, function () { console.log("server started at port 3000"); });先對項目結(jié)構(gòu)改造
iExpress/ | | | - application.js #包裹app層 | | - route/ | | - index.js #Router類 | | - route.js #Route類 | | - layer.js #Layer類 | | - middle/ | | - init.js #內(nèi)置中間件 | | - test/ | | - 測試用例文件1 | | - ... | ·- express.js #框架入口app從字面量變?yōu)锳pplication類
豐富HTTP請求方法
封裝Router
路徑一樣的路由整合為一組,引入Layer的概念
增加路由控制,支持next方法,并增加錯誤捕獲功能
執(zhí)行Router.handle的時候傳入out參數(shù)
理清邏輯
測試代碼中 注冊添加了多個路由且能添加多個回調(diào)方法,將邏輯分為三步。
(1)Application容器將請求方法和handler分發(fā)給router,在執(zhí)行l(wèi)isten監(jiān)聽函數(shù)時,執(zhí)行self._router.handle(req, res, done),讓塞入Router中的邏輯運(yùn)行。
Application類const Router = require("./router"); Application.prototype.lazyrouter = function () { if (!this._router) { this._router = new Router(); } } methods.forEach(function (method) { Application.prototype[method] = function () { this.lazyrouter(); //這樣寫可以支持多個處理函數(shù) this._router[method].apply(this._router, slice.call(arguments)); return this; } }); Application.prototype.listen = function () { let self = this; let server = http.createServer(function (req, res) { function done() {//如果沒有任何路由規(guī)則匹配的話會走此函數(shù) res.end(`Cannot ${req.method} ${req.url}`); } //如果路由系統(tǒng)無法處理,也就是沒有一條路由規(guī)則跟請求匹配,是會把請求交給done self._router.handle(req, res, done); }); server.listen(...arguments); }(2) 在Router中每一個方法的請求都會往當(dāng)前的路由系統(tǒng)中添加一個層,在層(layer)中創(chuàng)建一個route實(shí)例
Router類proto.route = function (path) { let route = new Route(path); let layer = new Layer(path, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer);//在Router中新增一層layer return route; } methods.forEach(function (method) { proto[method] = function (path) {//請求過來 let route = this.route(path);//往Router里添一層 route[method].apply(route, slice.call(arguments, 1));// return this; } });如果是中間件,默認(rèn)沒有path 所以layer的route設(shè)為undefined
proto.use = function (path, handler) { if (typeof handler != "function") { handler = path; path = "/"; } let layer = new Layer(path, handler); layer.route = undefined;//我們正是通過layer有沒有route來判斷是一個中間件函數(shù)還是一個路由 this.stack.push(layer); return this }Application開始監(jiān)聽端口時,執(zhí)行Router的handle方法。
添加 next
函數(shù)主要負(fù)責(zé)將控制權(quán)交給下一個中間件,如果當(dāng)前中間件沒有終結(jié)請求,并且next沒有被調(diào)用,那么請求將被掛起,后邊定義的中間件將得不到被執(zhí)行的機(jī)會。當(dāng)Router中的路徑和方法匹配時,走到當(dāng)前l(fā)ayer中,運(yùn)行l(wèi)ayer.handle_request 執(zhí)行route中添加的方法。
proto.handle = function (req, res, out) { //slashAdded是否添加過/ removed指的是被移除的字符串 let idx = 0, self = this, slashAdded = false, removed = ""; // /user/2 let { pathname } = url.parse(req.url, true); function next(err) { if (slashAdded) { req.url = ""; slashAdded = false; } if (removed.length > 0) { req.url = removed + req.url; removed = ""; } if (idx >= self.stack.length) { return out(err); } let layer = self.stack[idx++]; //在此匹配路徑 params 正則+url= req.params if (layer.match(pathname)) {// layer.params if (!layer.route) { //這一層是中間件層// /user/2 removed = layer.path;// /user req.url = req.url.slice(removed.length);// /2 if (err) { layer.handle_error(err, req, res, next); } else { if (req.url == "") { req.url = "/"; slashAdded = true; } layer.handle_request(req, res, next); } } else { if (layer.route && layer.route.handle_method(req.method)) { //把layer的parmas屬性拷貝給req.params req.params = layer.params; self.process_params(layer, req, res, () => { layer.handle_request(req, res, next); }); } else { next(err); } } } else { next(err); } } next(); }(3)進(jìn)入到當(dāng)前l(fā)ayer,按照順序執(zhí)行添加的每一個route
Layer類
Layer.prototype.handle_request = function (req, res, next) { this.handler(req, res, next); }注意 這里的this.handler方法,是添加layer時加入的route.dispatch.bind(route),dispatch是在router.route方法中,初始化layer的時候綁定到Layer.handler上的,解析下dispatch代碼:
Route.prototype.dispatch = function (req, res, out) { let idx = 0, self = this; function next(err) { if (err) {//如果一旦在路由函數(shù)中出錯了,則會跳過當(dāng)前路由 return out(err); } if (idx >= self.stack.length) { return out();//route.dispath里的out剛好是Router的next } let layer = self.stack[idx++]; if (layer.method == req.method.toLowerCase()) { layer.handle_request(req, res, next); } else { next(); } } next(); }文字結(jié)構(gòu)圖如下
Application | | Router | | - stack | | - Layer | | - path router | | - method handlerRouter
LayerRouter Layer 路徑 處理函數(shù)(route.dispatch) 有一個特殊的route屬性
Route layer 路徑 處理函數(shù)(真正的業(yè)務(wù)代碼) 有一特殊的屬性method
Application只做包裝幻術(shù)及路由分發(fā),
邏輯說明圖 源碼
Router實(shí)現(xiàn)
app.use、
app.param、
app.get、
app.post等路由方法方法的封裝倉庫地址:源碼鏈接點(diǎn)這里~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93437.html
摘要:五六月份推薦集合查看最新的請點(diǎn)擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點(diǎn)擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:我們在對現(xiàn)在較主流的五個文檔工具分別作了調(diào)研和嘗試,得到結(jié)論如下工具優(yōu)點(diǎn)缺點(diǎn)提供了完整的模板開發(fā)事件觸發(fā)等接口,使用非常靈活。至此,的環(huán)境部署已經(jīng)全部完成了。 字?jǐn)?shù):981 閱讀時間:5分鐘 選型依據(jù) ? 在經(jīng)歷了數(shù)個上線的項目之后,筆者所在的團(tuán)隊已經(jīng)沉淀了一個相對穩(wěn)定版本的前端框架。因此,我們需要出具一套框架API文檔,以便公司其他成員的使用和框架的后期維護(hù)。我們在對...
摘要:請注意,觸發(fā)器將不會在上午點(diǎn)開始,僅在,,和請注意,一些調(diào)度要求太復(fù)雜,無法用單一觸發(fā)表示例如每上午至之間每分鐘,下午至晚上點(diǎn)之間每分鐘一次。在這種情況下的解決方案是簡單地創(chuàng)建兩個觸發(fā)器,并注冊它們來運(yùn)行相同的作業(yè)。 表達(dá)式說明 Cron-Expressions 用于配置 CronTrigger的實(shí)例。Cron Expressions 是由七個子表達(dá)式組成的字符串,用于描述日程表的各個...
閱讀 3259·2021-10-21 17:50
閱讀 3265·2021-10-08 10:05
閱讀 3400·2021-09-22 15:04
閱讀 590·2019-08-30 14:00
閱讀 1952·2019-08-29 17:01
閱讀 1517·2019-08-29 15:16
閱讀 3228·2019-08-26 13:25
閱讀 860·2019-08-26 11:44