trpc 原理解析这或许是新的前端请求替代方案

css 复制代码
这里的trpc讲的是nodejs中的`@trpc/*`库

TRPC 是啥?

github 链接
官方介绍

arduino 复制代码
RPC 是 "远程过程调用" 的缩写。 它是一种从另一台计算机(客户端)调用一台计算机(服务器)上的函数的方法。 使用传统的 HTTP/REST API,你可以调用 URL 并获得响应。 使用 RPC,你可以调用函数并获得响应。

省流版

底层主要用了Proxy,通过递归使用Proxy记录属性的名称后生成接口地址

根据使用方式开局直接突进@trpc/client中的createTRPCClientProxy
Proxy文档
createTRPCClientProxy源码
trpc中的Proxy部分源码

实现 Demo

arduino 复制代码
这里只是提取源码写出一个小Demo,重点是类型和'Proxy'的配合,不从头开始写,也不写得就一模一样。

先简单声明一下接口后端路由对象

ts 复制代码
type UserItem = {
  uid: string;
  name: string;
  level: number;
};

const router = {
  user: {
    async info(uid: string): Promise<UserItem> {
      return {
        uid: "1111-2222",
        name: "WLQ",
        level: 3,
      };
    },
    a: {
      b() {},
    },
  },
};

export type ApiRouter = typeof router;

在前端中通过import type导入后端路由类型

ts 复制代码
import type { ApiRouter } from "@demo/server";

接着我们要实现createFlatProxy来为根路由创建代理对象

ts 复制代码
/** 这里使用函数类型,Proxy中才可以配置apply */
const noop: any = () => {};
ts 复制代码
/** 为根路由创建代理对象 */
function createFlatProxy<TRouter extends {}>() {
  return new Proxy<TRouter>(noop, {
    get(_obj, key) {
      if (typeof key !== "string" || key === "then") {
        return undefined;
      }

      // 这里返回一个代理过的函数
      return createRecursiveProxy([key]);
    },
    // 根路由不会被调用所以不需要配置apply
  });
}

createRecursiveProxy为子路由生成代理对象,并递归为子路由生成代理对象

通过代理对象的get记录路由路径

前期将noop赋值为一个函数,主要是为了可以触发apply,一触发便调用网络请求函数
apply的返回值将会是用户调用函数后的返回值

ts 复制代码
/**
 * 创建子路由代理对象,并在使用属性的时候递归创建代理对象
 * @param { string[] } paths 记录本次调用的路径
 */
function createRecursiveProxy(paths: string[]) {
  return new Proxy(noop, {
    get(_obj, name) {
      if (typeof name !== "string") {
        return undefined;
      }

      // 记录子路由
      paths.push(name);
      // 递归创建代理对象
      return createRecursiveProxy(paths);
    },
    // 当noop被调用,接收传递的参数
    apply(_1, _2, args) {
      // 拼接后端接口名称
      const path = paths.join(".");
      // 调用网络请求函数,并返回调用结果
      return requestApi(path, args);
    },
  });
}
ts 复制代码
/**
 * 网络请求函数
 *
 * 一般如果需要配置拦截器,过滤器,转换器,记录,日志...基本都在这里
 *
 * trpc中是可以通过link配置的
 * @param path 后端接口路径
 * @param args 前端调用时所传递的参数
 */
function requestApi(path: string, args: any[]) {
  // return fetch(/** 这里写上link上配置的请求属性*/ path)
  // 可以把完整的axios配置搬过来写这里
  // 这里我直接简单返回获取到的调用值就行了
  return {
    path,
    args,
  };
}

接着我们就可以开始愉快的使用ts了!!

ts 复制代码
const trpc = createFlatProxy<ApiRouter>();

console.log(trpc.user.info("1111-2222")); // { path: 'user.info', args: [ '1111-2222' ] }
console.log(trpc.user.a.b()); // { path: 'user.a.b', args: [] }

完整代码

ts 复制代码
type UserItem = {
  uid: string;
  name: string;
  level: number;
};

const router = {
  user: {
    /** trpc中Response接口类型基本无需配置 */
    async info(uid: string): Promise<UserItem> {
      return {
        uid: "1111-2222",
        name: "WLQ",
        level: 3,
      };
    },
    a: {
      b() {},
    },
  },
};

type ApiRouter = typeof router;

/** 这里使用函数类型,Proxy中才可以配置apply */
const noop: any = () => {};

/**
 * 网络请求方法
 *
 * 一般如果需要配置拦截器,过滤器,转换器,记录,日志...基本都在这里
 *
 * trpc中是可以通过link配置的
 * @param path 后端接口路径
 * @param args 前端调用时所传递的参数
 */
function requestApi(path: string, args: any[]) {
  // return fetch(/** 这里写上link上配置的请求属性*/ path)
  // 可以把完整的axios配置搬过来写这里
  // 这里我直接简单返回获取到的调用值就行了
  return {
    path,
    args,
  };
}

/**
 * 创建子路由代理对象,并在使用属性的时候递归创建代理对象
 * @param { string[] } paths 记录本次调用的路径
 */
function createRecursiveProxy(paths: string[]) {
  return new Proxy(noop, {
    get(_obj, name) {
      if (typeof name !== "string") {
        return undefined;
      }

      // 记录子路由
      paths.push(name);
      // 递归创建代理对象
      return createRecursiveProxy(paths);
    },
    // 当noop被调用,接收传递的参数
    apply(_1, _2, args) {
      // 拼接后端接口名称
      const path = paths.join(".");
      // 调用网络请求方法,并返回调用结果
      return requestApi(path, args);
    },
  });
}

/** 为根路由创建代理对象 */
function createFlatProxy<TRouter extends {}>() {
  return new Proxy<TRouter>(noop, {
    get(_obj, key) {
      if (typeof key !== "string" || key === "then") {
        return undefined;
      }

      // 这里返回一个代理过的函数
      return createRecursiveProxy([key]);
    },
    // 根路由不会被调用所以不需要配置apply
  });
}

const trpc = createFlatProxy<ApiRouter>();

console.log(trpc.user.info("1111-2222")); // { path: 'user.info', args: [ '1111-2222' ] }
console.log(trpc.user.a.b()); // { path: 'user.a.b', args: [] }

结尾随谈

如果后端愿意将接口文档写成ServerApiRouter.d.ts在通过这套方法实现前端的请求方式,这可为给前端减轻了不少的体力活,无需再去重复的声明接口函数,无需在去定义接口返回的数据类型。

相关推荐
brzhang4 分钟前
A2UI:但 Google 把它写成协议后,模型和交互的最后一公里被彻底补全
前端·后端·架构
coderHing[专注前端]13 分钟前
告别 try/catch 地狱:用三元组重新定义 JavaScript 错误处理
开发语言·前端·javascript·react.js·前端框架·ecmascript
UIUV29 分钟前
JavaScript中this指向机制与异步回调解决方案详解
前端·javascript·代码规范
momo10029 分钟前
IndexedDB 实战:封装一个通用工具类,搞定所有本地存储需求
前端·javascript
liuniansilence29 分钟前
🚀 高并发场景下的救星:BullMQ如何实现智能流量削峰填谷
前端·分布式·消息队列
再花29 分钟前
在Angular中实现基于nz-calendar的日历甘特图
前端·angular.js
GISer_Jing42 分钟前
今天看了京东零售JDS的保温直播,秋招,好像真的结束了,接下来就是论文+工作了!!!加油干论文,学&分享技术
前端·零售
Mapmost1 小时前
【高斯泼溅】如何将“歪头”的3DGS模型精准“钉”在地图上,杜绝后续误差?
前端
废春啊1 小时前
前端工程化
运维·服务器·前端
爱上妖精的尾巴1 小时前
6-9 WPS JS宏Map、 set、get、delete、clear()映射的添加、修改、删除
前端·wps·js宏·jsa