Skip to main content

Koa2快速构建BFF应用OOP版🤔

🚗 基本操作

首先先要分清楚 SSR CSR,MVC

yarn init


🚗 启动服务

yarn add koa -S

https://www.npmjs.com/package/koa

Koa2 API 很重要,要仔细阅读:

https://koajs.com

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

// response
app.use((ctx) => {
ctx.body = "Hello Koa";
});

app.listen(3000, () => {
console.log("服务已启动🚀🚀🚀🚀🚀");
});

🚗 注册路由(C)

yarn add @koa/router -S

https://www.npmjs.com/package/koa-router

koa-router API 很重要仔细阅读

https://github.com/koajs/router/blob/master/API.md

koa-router

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

const Router = require("@koa/router");
const router = new Router();

// 换个路由试试
router.get("/", (ctx, next) => {
ctx.body = "<h1>Hello Koa - router</h1>";
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
console.log("服务已启动🚀🚀🚀🚀🚀");
});

但是在实际开发中这样写路由是不行的:

Controllers

// ./controllers/index.js
const Router = require("@koa/router");
const router = new Router();

router.get("/", (ctx, next) => {
ctx.body = "<h1>Hello Koa - router</h1>";
});

module.exports = (app) => {
app.use(router.routes()).use(router.allowedMethods());
};
// ./app.js
require("./controllers")(app);

在进行封装

// ./controllers/IndexController.js
class IndexController {
constructor() {}
actionIndex() {
return async (ctx, next) => {
ctx.body = "<h1>Hello Koa - routercv</h1>";
};
}
}

module.exports = IndexController;
// ./controllers/index.js
const Router = require("@koa/router");
const router = new Router();

// index
const IndexController = require("./IndexController");
const indexController = new IndexController();
router.get("/", indexController.actionIndex());

module.exports = (app) => {
app.use(router.routes()).use(router.allowedMethods());
};

🚗 启动服务升级版

设置环境

yarn add cross-env -S

https://www.npmjs.com/package/cross-env

  "scripts": {
"dev": "cross-env NODE_ENV=development node ./app.js"
},

console.log("🍎🍎🍎🍎 ", process.env.NODE_ENV);

配置文件 config

// ./config/index.js
let config = {};

if (process.env.NODE_ENV == "development") {
const baseConfig = {
ip: "localhost",
host: 3000,
};
Object.assign(config, baseConfig);
}

module.exports = config;
// ./app.js
const Koa = require("koa");
const app = new Koa();
const config = require("./config/index");
require("./controllers")(app);

app.listen(config.host, () => {
console.log(
`服务已启动🚀🚀🚀🚀🚀 ===== http://${config.ip}:${config.host}`
);
});

热启动

node-supervisor

sudo yarn add supervisor -g

https://www.npmjs.com/package/supervisor

  "scripts": {
"dev": "cross-env NODE_ENV=development node-supervisor ./app.js"
},

pm2

==预留==

🚗 模板引擎 (V)

koa-swig && ejs

koa-swig

yarn add koa-swig -S

koa-swig 性能突出,但是没人维护了

https://github.com/koa-modules/swig#readme

koa-swig 模板语言语法

https://www.jianshu.com/p/82cade205875

// ./app.js
const { join } = require("path");
const render = require("koa-swig");
const co = require("co");
app.context.render = co.wrap(
render({
// 查看根目录
root: join(config.viewDir),
// ???
autoescape: true,
// 缓存
cache: config.cacheMode,
// 默认视图外命名
ext: "html",
//
varControls: ["[[", "]]"],
// 默认(true)自动写入正文和响应
writeBody: false,
// 工具函数引入
filters: require("./utils/SwigFilters.js"),
})
);
// ./config/index.js
const { join } = require("path");

let config = {
viewDir: join(__dirname, "..", "views"),
};

if (process.env.NODE_ENV == "development") {
const baseConfig = {
cacheMode: false,
ip: "localhost",
host: 3000,
};
Object.assign(config, baseConfig);
}

module.exports = config;
// ./utils/SwigFilters.js
exports.text1 = (e) => {
return `${e}----text1`;
};

./views/web.html 主模板内容

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>{% block title %}{% endblock %}</title>
{% block head %}{% endblock %}
</head>

<body>
{% block content %}{% endblock %} {% block scripts %}{% endblock %}
</body>
</html>

./view/index/index.html 路由模板

{% extends '../web.html' %} {% block title %}🤔KOA2快速构架BFF应用🤔{% endblock
%} {% block head %} {% endblock %} {% block content %}
<header></header>

<main>
<h1>🤔KOA2快速构架BFF应用🤔</h1>
<h2>[[data]]</h2>
<h2>[[data | text1()]]</h2>
</main>

<footer></footer>
{% endblock %} {% block scripts %} {% endblock %}

controller 中输出

const html = await ctx.render("index/index", {
data: "IndexController",
});
ctx.body = html;

导入静态文件

yard add koa-static -S

https://www.npmjs.com/package/koa-static

// ./app.js
const serve = require("koa-static");
app.use(serve(".")); //待研究 指定某一目录
/* ./assets/css/index.css */
h1 {
color: salmon;
}
h2 {
color: yellowgreen;
}
<link rel="stylesheet" href="../../assets/css/index.css" />

🚗 数据模型 (M)

node-fetch

yarn add node-fetch@2 -S

https://www.npmjs.com/package/node-fetch

baseURL: 'https://devwww.laoyaoba.com/api',
// ./utils/SafeRequest.js
const fetch = require("node-fetch");
const config = require("../config");

class SafeRequest {
constructor(url) {
this.url = url;
this.baseURL = config.baseURL;
}
async fetch(options) {
let _fetch;

if (options.params) {
_fetch = fetch(this.baseURL + this.url, {
method: options.method,
body: options.params,
});
} else {
_fetch = fetch(this.baseURL + this.url);
}

return await new Promise((resolve, reject) => {
let result = {
message: "node-fetch:后端接口返回成功",
data: [],
};

_fetch
.then((res) => res.json())
.then((json) => {
if (json.errno == 0) {
result.data = json;
resolve(result);
} else {
result.data = json;
result.message = "node-fetch:后端返回数据异常";
result.api = this.baseURL + this.url;
result.params = options.params.toString();
reject(result);
}
})
.catch((error) => {
result.data = error;
result.message = "node-fetch:node-fetch和后端通讯异常";
result.api = this.baseURL + this.url;
result.params = options.params.toString();
reject(result);
});
});
}
}

module.exports = SafeRequest;
// ./models/IndexModel.js
const SafeRequest = require("../utils/SafeRequest.js");
class IndexModel {
constructor(app) {}
getFeedStream(options) {
const safeRequest = new SafeRequest("/news/feedstream");
return safeRequest.fetch({
method: "POST",
params: options.params,
});
}
}
module.exports = IndexModel;
// ./controllers/IndexController.js
const IndexModel = require("../models/IndexModel");
const indexModel = new IndexModel();
const { URLSearchParams } = require("url");
class IndexController {
constructor() {}
actionIndex() {
return async (ctx, next) => {
const result = await this.getData();
const html = await ctx.render("index/index", {
data: "IndexController",
list: result,
});
ctx.body = html;
};
}

async getData() {
try {
const params = await new URLSearchParams();
params.append("source", "pc");
const result = await indexModel.getFeedStream({
params,
});

return result.data.data;
} catch (e) {}
}
}

module.exports = IndexController;
{% for key, val in list %}
<h3>[[val.news_title]]</h3>
{% endfor %}

🚗 中间件