76 # koa 上下文的实现原理

上一节实现了 koa 基本逻辑实现以及属性的扩展,下面继续实现上下文的实现

ctx 跟 proto 的关系

ctx.__proto__.__proto__ = proto

MDN:defineGetter

备注: 此特性已弃用,建议使用对象初始化语法或 Object.defineProperty() API 来定义 getter。该方法的行为只针对 Web 兼容性进行了规定,在任何平台上都不需要实现该方法。它可能无法在所有地方正常工作。

MDN:defineSetter

备注: 此特性已弃用,建议使用对象初始化语法或 Object.defineProperty() API 来定义 setter。该方法的行为只针对 Web 兼容性进行了规定,在任何平台上都不需要实现该方法。它可能无法在所有地方正常工作。

  • __defineGetter__() 方法将一个对象的属性绑定到一个函数上,当该属性被访问时,该函数将被调用。
  • __defineSetter__() 方法将一个对象的属性绑定到一个函数上,当该属性被赋值时,该函数将被调用。
__defineGetter__(prop, func)
__defineSetter__(prop, func)

以后使用 ctx 变量时,会很少使用原生的 req 和 res,一般使用的都是 request,reponse,或者直接使用的方式。

下面代码实现:

application.js

const EventEmitter = require("events");
const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");

console.log("kaimo-koa---->");

class Application extends EventEmitter {
    
    
    constructor() {
    
    
        super();
        // 防止多个实例共享 context request response 需要进行拷贝
        this.context = Object.create(context);
        this.request = Object.create(request);
        this.response = Object.create(response);
    }
    use(callback) {
    
    
        this.callback = callback;
    }
    // 创建一个上下文
    createContext(req, res) {
    
    
        // 每次请求都应该是一个全新的 context,需要拷贝
        let ctx = Object.create(this.context);
        // 上下文中有一个 request 对象,是自己封装的
        ctx.request = Object.create(this.request);
        // 上下文中还有一个 req 属性 指代的是原生的 req,自己封装的 request 对象上有 req 属性
        ctx.req = ctx.request.req = req;
        // 上下文中还有一个 response 对象,是自己封装的
        ctx.response = Object.create(this.response);
        // 上下文中还有一个 res 属性 指代的是原生的 res,自己封装的 response 对象上有 res 属性
        ctx.res = ctx.response.res = res;
        return ctx;
    }
    handleRequest(req, res) {
    
    
        const ctx = this.createContext(req, res);
        this.callback(ctx);
    }
    listen(...args) {
    
    
        const server = http.createServer(this.handleRequest.bind(this));
        server.listen(...args);
    }
}

module.exports = Application;

上下文 context.js

const proto = {
    
    
    // get url() {
    
    
    //     console.log(this.__proto__.__proto__ === proto);
    //     return this.request.url;
    // },
    // get path() {
    
    
    //     return this.request.path;
    // }
};
// 上面一个一个写比较麻烦,可以使用 __defineGetter__ 去实现代理
function defineGetter(target, key) {
    
    
    proto.__defineGetter__(key, function () {
    
    
        return this[target][key];
    });
}

function defineSetter(target, key) {
    
    
    proto.__defineSetter__(key, function (value) {
    
    
        this[target][key] = value;
    });
}

defineGetter("request", "url"); // ctx.url => ctx.request.url
defineGetter("request", "path"); // ctx.path => ctx.request.path
defineGetter("request", "query"); // ctx.query => ctx.request.query

defineGetter("response", "body"); // ctx.body => ctx.response.body
defineSetter("response", "body"); // ctx.body => ctx.response.body

module.exports = proto;

response.js

const response = {
    
    
    _body: "",
    get body() {
    
    
        return this._body;
    },
    set body(value) {
    
    
        this._body = value;
    }
};

module.exports = response;

测试 demo.js

const Koa = require("./kaimo-koa");
const app = new Koa();

app.use(async (ctx, next) => {
    
    
    ctx.body = "Hello kaimo Koa";
    console.log("url---->", ctx.request.url);
    console.log("path---->", ctx.request.path);
    console.log("query---->", ctx.request.query);
    // ctx.__proto__.__proto__ === proto
    console.log("ctx.url---->", ctx.url);
    console.log("ctx.path---->", ctx.path);
    console.log("ctx.query---->", ctx.query);
    // ctx.body => ctx.response.body
    console.log("ctx.response.body---->", ctx.response.body);
});

app.on("error", (err) => {
    
    
    console.log(err);
});

app.listen(3000);

启动服务,访问 http://localhost:3000/kaimo?a=313

nodemon demo.js

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/kaimo313/article/details/132680391
76
koa