成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

五分鐘輕仿一套express源碼

617035918 / 1669人閱讀

摘要:上面代碼的關(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ù),在原型上添加 getlisten方法

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;i

Express框架建立在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  handler
            

Router
Layer

Router 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

相關(guān)文章

  • 關(guān)于Vue2一些值得推薦的文章 -- 、六月份

    摘要:五六月份推薦集合查看最新的請點(diǎn)擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...

    sutaking 評論0 收藏0
  • 關(guān)于Vue2一些值得推薦的文章 -- 、六月份

    摘要:五六月份推薦集合查看最新的請點(diǎn)擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...

    khs1994 評論0 收藏0
  • 分鐘玩轉(zhuǎn)文檔化工具JSDuck

    摘要:我們在對現(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ù)。我們在對...

    rickchen 評論0 收藏0
  • Quartz學(xué)習(xí)之Cron Expressions表達(dá)式

    摘要:請注意,觸發(fā)器將不會在上午點(diǎn)開始,僅在,,和請注意,一些調(diào)度要求太復(fù)雜,無法用單一觸發(fā)表示例如每上午至之間每分鐘,下午至晚上點(diǎn)之間每分鐘一次。在這種情況下的解決方案是簡單地創(chuàng)建兩個觸發(fā)器,并注冊它們來運(yùn)行相同的作業(yè)。 表達(dá)式說明 Cron-Expressions 用于配置 CronTrigger的實(shí)例。Cron Expressions 是由七個子表達(dá)式組成的字符串,用于描述日程表的各個...

    Mr_zhang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<