Skip to content

Koa ​

一、 Kao、MVC、Node中间层、上层设计 ​

Koa 基于 Node.js 平台的下一代 web 开发框架。

1. Koa 简介 ​

Kao 是一个新的 web 框架,由 Express 幕后的原班人马打造,致力于成为 web 应用和 API 开发领域中一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件,而是提供了一套优雅的解决方案,帮助您快速而愉快地编写服务端应用程序。

Kao 是无上层设计的框架。 EggJS 是基于 Koa 开发的上层框架(MVC 开发模式的上层设计)。

2. MVC ​

2.1 M(model) 模型 ​

操作数据表/请求数据 (JAVA PHP PYTHON) 前端向 NODE(中间层) 层请求数据,NODE 再向纯后端请求数据。

为什么需要 NODE 做中间层?

  • 首屏服务端渲染(server side rendering)。 前端页面是一个完整页面(非JS在前端获取数据再组装页面)SEO不好。 Node 作为后端可以组装页面,可以直接将页面返回前端,不需要额外操作。

  • Node 发展较晚、数据接口基本都是传统后端语言编写,没有必要用 Node 重构。 Node 不适用高性能运算、数据分析、访问量大的场景(相比较Java、优势不大)。

为什么不用 JAVA 组装页面?

  • 本身希望前后端是分离的,如果使用 JAVA 组装页面,还需要负责样式、JS。
  • Node 使用 JS、TS 编写的,完全可以由前端来组装页面。 后端只负责接口开发,只负责数据部分。 前端负责交互、后端负责数据。

2.2 V(view)视图 ​

HTML、视图模板。

2.3 C(controller)控制器 ​

数据接口、提供视图数据。

二、Koa用法、Node中间层与前端渲染的区别 ​

js
npm i koa -D
npm i koa -D

listen 监听服务 use 注册中间件函数 on 监听事件

js
const Kao = require('koa');

const app = new Kao();

app.use((ctx, next) => {
  ctx.body = 'Hello Koa.';
});

app.listen(3000);
const Kao = require('koa');

const app = new Kao();

app.use((ctx, next) => {
  ctx.body = 'Hello Koa.';
});

app.listen(3000);

ctx 是 Koa 的执行期上下文。 req res 都在 ctx 中。原生。 ctx.request ctx.response。封装。 ctx.body === ctx.response.body

三、了解Koa的设计、搭建Koa源码架子 ​

js
// main - lib - application.js
const Koa = require('koa');

// class Application 实例化
const app = new Koa();

/**
 * 三个重要的app对象下的API
 * listen 监听服务
 * use:注册中间件函数
 * on:监听事件
 */

/**
 * @param {object} ctx - 执行期上下文
 *  包含执行期所需要的所有方法和属性
 *  例如:request、response
 */
app.use((ctx) => {
  console.log(ctx.path); // 封装 代理到 ctx.request.path
  console.log(ctx.request.path); // 封装
  console.log(ctx.request.req.url); // 原生
  console.log(ctx.req.url); // 原生
});

app.listen(3000);
// main - lib - application.js
const Koa = require('koa');

// class Application 实例化
const app = new Koa();

/**
 * 三个重要的app对象下的API
 * listen 监听服务
 * use:注册中间件函数
 * on:监听事件
 */

/**
 * @param {object} ctx - 执行期上下文
 *  包含执行期所需要的所有方法和属性
 *  例如:request、response
 */
app.use((ctx) => {
  console.log(ctx.path); // 封装 代理到 ctx.request.path
  console.log(ctx.request.path); // 封装
  console.log(ctx.request.req.url); // 原生
  console.log(ctx.req.url); // 原生
});

app.listen(3000);
js
const http = require('http');
const context = require('./context');
const request = require('./request');
const response = require('./response');

class Application {
  constructor () {
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
  }

  use (callback) {
    this.callback = callback;
  }

  listen (...args) {
    const httpService = http.createServer(this.handleHttpRequest.bind(this));
    httpService.listen(...args);
  }

  handleHttpRequest (req, res) {
    const ctx = this.createContext(req, res);
    this.callback(ctx);
  }

  createContext (req, res) {
    const ctx = Object.create(this.context);
    ctx.request = Object.create(this.request);
    ctx.response = Object.create(this.response);
    ctx.request.req = ctx.req = req;
    ctx.response.res = ctx.res = res;
    return ctx;
  }
}

module.exports = Application;
const http = require('http');
const context = require('./context');
const request = require('./request');
const response = require('./response');

class Application {
  constructor () {
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
  }

  use (callback) {
    this.callback = callback;
  }

  listen (...args) {
    const httpService = http.createServer(this.handleHttpRequest.bind(this));
    httpService.listen(...args);
  }

  handleHttpRequest (req, res) {
    const ctx = this.createContext(req, res);
    this.callback(ctx);
  }

  createContext (req, res) {
    const ctx = Object.create(this.context);
    ctx.request = Object.create(this.request);
    ctx.response = Object.create(this.response);
    ctx.request.req = ctx.req = req;
    ctx.response.res = ctx.res = res;
    return ctx;
  }
}

module.exports = Application;

四、请求拦截与上下文代理方案 ​

js
const { defineGetter } = require('./utils');
const { PROXY_REQUEST_GETTER_KEYS } = require('./config');

const proto = {};

const defineRequestGetter = defineGetter(proto, 'request');

PROXY_REQUEST_GETTER_KEYS.forEach(key => {
  defineRequestGetter(key);
})

module.exports = proto;

/**
 * ctx.path -> ctx.request.path
 * ctx.url -> ctx.request.url
 * Object.defineProperty
 *  Object.__defineGetter__ 拦截获取值
 *  Object.__defineSetter__ 拦截设置值
 */
const { defineGetter } = require('./utils');
const { PROXY_REQUEST_GETTER_KEYS } = require('./config');

const proto = {};

const defineRequestGetter = defineGetter(proto, 'request');

PROXY_REQUEST_GETTER_KEYS.forEach(key => {
  defineRequestGetter(key);
})

module.exports = proto;

/**
 * ctx.path -> ctx.request.path
 * ctx.url -> ctx.request.url
 * Object.defineProperty
 *  Object.__defineGetter__ 拦截获取值
 *  Object.__defineSetter__ 拦截设置值
 */
js
const url = require('url');

module.exports = {
  get path () {
    return url.parse(this.req.url).pathname;
  },

  get url () {
    return this.req.url
  }
}
const url = require('url');

module.exports = {
  get path () {
    return url.parse(this.req.url).pathname;
  },

  get url () {
    return this.req.url
  }
}

五、响应拦截、响应代理、处理响应内容 ​