node 框架详解

163 阅读2分钟

express/koa

函数式编程

开始前需要理解纯函数

 function demo(total) {
   return total + 1;
 }
 demo(1) // 2
 不管执行多少次,只要我传入的参数是1,输出的结果都是2demo(100) // 101
 不管执行多少次,只要我传入的参数是100,输出的结果都是101

总结:不会产生副作用的函数。输入是值确定,输出值一定是一样的。(这个总结并不好,会让人误解。之前看到一个博客,总结的很好,现在找不到了)

本章的核心,这个也是 koa的核心

不解释版

next 代表下一个函数, next(total * 0.8) 代表执行下一个函数。每一个函数都是纯函数。

  function discount(total, next) {
    next(total * 0.8);
  }
   function express(total, next) {
    next(total + 12);
  }
   function num(total, next) {
    next(total * 10);
  }
  
  function compose(args) {
    let result;
    return function(ctx) {
      let i = 0;
      let dispatch = function (i, ctx) {
         let fn;
         if(i < args.length) fn = args[i]; 
         if(i === args.length) {
           result = ctx;
           return;
         }
         return fn(ctx, dispatch.bind(null, ++i);
      }
      dispatch(0, ctx); 
      return result;
    }
  }
  
  
  const totalMoney = compose([num, discount, express])(15);
  

解释版

// next 代表下一个函数, next(total * 0.8) 代表执行下一个函数。每一个函数都是纯函数。

  function discount(total, next) {
    console.log("discount start")
    next(total * 0.8);
    console.log("discount end")
  }
   function express(total, next) {
    console.log("express start")
    next(total + 12);
    console.log("express end")
  }
   function num(total, next) {
    console.log("num start")
    next(total * 10);
    console.log("num end")
  }
  
  function compose(args) {
    let result;
    return function(ctx) {
      let i = 0;
      // ** 重点 ** start
      // 这里是递归,fn 依次是num, discount, express
      let dispatch = function (i, ctx) {
         let fn;
         if(i < args.length) fn = args[i]; 
         if(i === args.length) {
           result = ctx;
           return;
         }
         // fn 是 num, discount, express 函数。比如当前是 num, ctx 是 15
         // bind函数调用时,除了第一个参数,其余参数将作为新函数的参数,供调用时使用。
         // dispatch.bind(null, ++i) 是下一个参数 discount。递归从这里开始。
         return fn(ctx, dispatch.bind(null, ++i);
      }
       // ** 重点 ** end
      dispatch(0, ctx); // 第一步。也是洋葱模型的核心,梦开始的地方。
      return result;
    }
  }
  
  
  const totalMoney = compose([num, discount, express])(15); 
  // num start
  // discount start
  // express start
  // express end
  // discount end 
  // num end

上面的代码还可以再改进下,比如: 再调用其他函数,使用promise。这里就不展示了

KOA

核心代码在 koa/lib/application

// 洋葱模型,跟前面写的一样,但是多了 promise 这一步
app.use((ctx, next) => {
  ...todo
  next();
  ...todo
})
app.listen(8000, '', ()=> {});

BFF

BFF 本质上是一种架构分层,而不是一种技术

BFF作用有哪些:

对比传统的架构,BFF的优势:
  • 降低沟通成本,领域模型可以与页面 更好的解耦;
  • 提供更好的用户体验,多端适配场景,数据会更加的复合交互需求。
BFF 的劣势
  • 分工问题, 作为灰色地带,谁来开发, 需要明确。
  • 链路复杂, 引入BFF,流程也更繁琐
  • BFF 占用一部分资源
设计一个BFF 的时候,需要考虑哪些问题
  1. 数据处理: 数据耦合和裁剪 序列化格式转换 协议转换
  2. 流量处理: 请求分发 代理 削峰 熔断
  3. 安全: fast-gateway

Sequelize

Sequelize 是基于 Node 端的ORM,目前支持: Mysql, postgres, SQLite, SQLServer.... ORM: 帮你生成SQL,对象关系映射,映射成一个模型。

  • typeORM
    • typeORM 比较适用于使用了 ts 的场景
  • prisma
    • 在类型推导上,更加地出色
    • rust 写的查询引擎
    • 有一套非常成熟的 dsl

跟上一次写的 es 实战 结合起来,rollup(路由前缀) + sequelize .sequelizerc 文件设置路径(不加 .js 后缀),

npx sequelize --help 查看命令

  1. npx sequelize init:config , 根据.sequelizerc 自动生成文件
  2. npx sequelize migration:generate --name=init-powers(格式 --name=xxx-xxx) 生成 name 结尾的 js 文件
  3. 文件里有 up,和 down两个函数,在进行数据迁移时, async up (queryInterface, Sequelize) // 创建的表 async down (queryInterface, Sequelize) // 删除的表

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up (queryInterface, Sequelize) {
    await queryInterface.createTable('roles', { 
      id: { type: INTEGER, primaryKey: true },
      userid: INTEGER,
      roleName: STRING(30), 
    });
  },

  async down (queryInterface, Sequelize) {
    await queryInterface.dropTable('roles');
  }
};
  1. npx sequelize db:migrate 运行起来。 要装 mysql 之类的数据库,不然会报错。

我们也可以自动生成模型, 使用 sequelize-auto,网上也有,这里不写了。