上一节实现了 koa 基本逻辑实现以及属性的扩展,下面继续实现上下文的实现
ctx 跟 proto 的关系
ctx.__proto__.__proto__ = proto
备注: 此特性已弃用,建议使用对象初始化语法或
Object.defineProperty()API 来定义 getter。该方法的行为只针对 Web 兼容性进行了规定,在任何平台上都不需要实现该方法。它可能无法在所有地方正常工作。
备注: 此特性已弃用,建议使用对象初始化语法或 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
