css
这里的trpc讲的是nodejs中的`@trpc/*`库
TRPC 是啥?
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
在通过这套方法实现前端的请求方式,这可为给前端减轻了不少的体力活,无需再去重复的声明接口函数,无需在去定义接口返回的数据类型。