上一节实现了 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