小法度楷模云斥地教程之云函数路由高级弄法
在掘金斥地者除夜会上,在举荐实践那儿何处,我有提到一种云函数的用法,我们可以将不异的一些操作,好比用户治理、支出逻辑,遵循营业的近似性,归类到一个云函数里,这样斗劲便当治理、排盘问题和逻辑的共享。甚至假定你的小法度楷模的后台逻辑不复杂,要求量不是出格除夜,完全可以在云函数里面做一个单一的微处事,遵循路由来措置使命。
tcb-router 介绍及用法
为了便当巨匠试用,我们腾讯云 Tencent Cloud Base 团队斥地了 tcb-router,云函数路由治理库便当巨匠操作。
那具体若何操作 tcb-router 去实现上面提到的架构呢?下面我会一一举例子。
架构一:一个云函数措置一个使命
这类架构下,其实不需要用到 tcb-router,像通俗那样写好云函数,然后在小法度楷模端挪用便可以了。
- 云函数
// 函数 router exports.main = (event, context) => { return { code: 0, message: 'success' }; };
- 小法度楷模端
wx.cloud.callFunction({ name: 'router', data: { name: 'tcb', company: 'Tencent' } }).then((res) => { console.log(res); }).catch((e) => { console.log(e); });
架构二: 按要求给云函数归类
此类架构就是快要似的要求归类到统一个云函数措置,好比可以分为用户治理、支出等等的云函数。
- 云函数
// 函数 user const TcbRouter = require('tcb-router'); exports.main = async (event, context) => { const app = new TcbRouter({ event }); app.router('register', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'register success' } }); app.router('login', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'login success' } }); return app.serve(); }; // 函数 pay const TcbRouter = require('tcb-router'); exports.main = async (event, context) => { const app = new TcbRouter({ event }); app.router('makeOrder', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'make order success' } }); app.router('pay', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'pay success' } }); return app.serve(); };
- 小法度楷模端
// 注册用户 wx.cloud.callFunction({ name: 'user', data: { $url: 'register', name: 'tcb', password: '09876' } }).then((res) => { console.log(res); }).catch((e) => { console.log(e); }); // 下单商品 wx.cloud.callFunction({ name: 'pay', data: { $url: 'makeOrder', id: 'xxxx', amount: '3' } }).then((res) => { console.log(res); }).catch((e) => { console.log(e); });
架构三: 由一个云函数措置所有处事
- 云函数
// 函数 router const TcbRouter = require('tcb-router'); exports.main = async (event, context) => { const app = new TcbRouter({ event }); app.router('user/register', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'register success' } }); app.router('user/login', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'login success' } }); app.router('pay/makeOrder', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'make order success' } }); app.router('pay/pay', async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx) => { ctx.body = { code: 0, message: 'pay success' } }); return app.serve(); };
- 小法度楷模端
// 注册用户 wx.cloud.callFunction({ name: 'router', data: { $url: 'user/register', name: 'tcb', password: '09876' } }).then((res) => { console.log(res); }).catch((e) => { console.log(e); }); // 下单商品 wx.cloud.callFunction({ name: 'router', data: { $url: 'pay/makeOrder', id: 'xxxx', amount: '3' } }).then((res) => { console.log(res); }).catch((e) => { console.log(e); });
借鉴 Koa2 的中心件机制实现云函数的路由治理
小法度楷模·云斥地的云函数今朝更举荐 async/await 的弄法来措置异步操作,是以这里也参考了一样是基于 async/await 的 Koa2 的中心件实现机制。
从上面的一些例子我们可以看出,主若是经由过程 use 和 router 两种编制传入路由和相关措置的中心件。
use 只能传入一个中心件,路由也只能是字符串,凡是用于 use 一些所有路由都得操作的中心件
// 不写路由暗示该中心件操作于所有的路由 app.use(async (ctx, next) => { }); app.use('router', async (ctx, next) => { });
router 可以传一个或多个中心件,路由也能够传入一个或多个。
app.router('router', async (ctx, next) => { }); app.router(['router', 'timer'], async (ctx, next) => { await next(); }, async (ctx, next) => { await next(); }, async (ctx, next) => { });
不外,不管是 use 仍是 router,都只是将路由和中心件信息,经由过程 _addMiddleware 和 _addRoute 两个体例,录入到 _routerMiddlewares 该对象中,用于后续挪用 serve 的时辰,层层去履行中心件。
最首要的运行中心件逻辑,则是在 serve 和 compose 两个体例里。
serve 里首要的浸染是做路由的匹配和将中心件组合好往后,经由过程 compose 进行下一步的操作。好比以下这段节选的代码,现实上是将匹配到的路由的中心件,和 * 这个通配路由的中心件合并到一路,最后顺次履行。
let middlewares = (_routerMiddlewares[url]) ? _routerMiddlewares[url].middlewares : []; // put * path middlewares on the queue head if (_routerMiddlewares['*']) { middlewares = [].concat(_routerMiddlewares['*'].middlewares, middlewares); }
组合好中心件后,履行这一段,将中心件 compose 后并返回一个函数,传入上下文 this 后,最后将 this.body 的值 resolve,即一般在最后一个中心件里,经由过程对 ctx.body 的赋值,实现云函数的对小法度楷模端的返回:
const fn = compose(middlewares); return new Promise((resolve, reject) => { fn(this).then((res) => { resolve(this.body); }).catch(reject); });
那么 compose 是若何组合好这些中心件的呢?这里截取部门代码进行分化
function compose(middleware) { /** * ... 其它代码 */ return function (context, next) { // 这里的 next,假定是在主流程里,一般 next 都是空。 let index = -1; // 在这里最早措置措置第一个中心件 return dispatch(0); // dispatch 是焦点的编制,经由过程不竭地挪用 dispatch 来措置所有的中心件 function dispatch(i) { if (i <= index) { return Promise.reject(new Error('next() called multiple times')); } index = i; // 获得中心件函数 let handler = middleware[i]; // 措置完最后一个中心件,返回 Proimse.resolve if (i === middleware.length) { handler = next; } if (!handler) { return Promise.resolve(); } try { // 在这里不竭地挪用 dispatch, 同时增添 i 的数值措置中心件 return Promise.resolve(handler(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err); } } } }
看完这里的代码,其实有点迷惑,若何经由过程 Promise.resolve(handler(xxxx)) 这样的代码逻辑可以敦促中心件的挪用呢?
首先,我们知道,handler 其实就是一个 async function,next,就是 dispatch.bind(null, i + 1) 好比这个:
async (ctx, next) => { await next(); }
而我们知道,dispatch 是返回一个 Promise.resolve 或一个 Promise.reject,是以在 async function 里履行 await next(),就相当于触发下一个中心件的挪用。
当 compose 完成后,仍是会返回一个 function (context, next),因而就走到下面这个逻辑,履行 fn 并传入上下文 this 后,再将在中心件中赋值的 this.body resolve 出来,事实下场就成为云函数数要返回的值。
const fn = compose(middlewares); return new Promise((resolve, reject) => { fn(this).then((res) => { resolve(this.body); }).catch(reject); });
看到 Promise.resolve 一个 async function,良多人城市很思疑。其实撇除 next 这个往下挪用中心件的逻辑,我们可以很好地将逻辑简化成下面这段示例:
let a = async () => { console.log(1); }; let b = async () => { console.log(2); return 3; }; let fn = async () => { await a(); return b(); }; Promise.resolve(fn()).then((res) => { console.log(res); }); // 输出 // 1 // 2 // 3
第二部门:若何开通一个小商铺