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