惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
NISL@THU
NISL@THU
E
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
K
Kaspersky official blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
S
Schneier on Security
G
GRAHAM CLULEY
The Hacker News
The Hacker News
T
Threat Research - Cisco Blogs
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
爱范儿
爱范儿
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
S
Securelist
G
Google Developers Blog
The Last Watchdog
The Last Watchdog
Google Online Security Blog
Google Online Security Blog
美团技术团队
F
Fortinet All Blogs
小众软件
小众软件
Recorded Future
Recorded Future
V
Visual Studio Blog
B
Blog RSS Feed
H
Help Net Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
Latest news
Latest news
Spread Privacy
Spread Privacy
H
Heimdal Security Blog

OK Computer

【RPC】如何封装一个远程过程调用 - 实现迷你全栈 rpc 框架 【Cesium】基于 JavaScript 面向对象特性管理 Cesium Primitives 分析 Vue3 Reactivity 的一个重构 PR 前端使用 Web Worker 和 OffscreenCanvas 异步生成图片 【羽毛球】分析巅峰时期的桃田贤斗为什么如此具有统治力 【Cesium】在不显示地形的情况下获取地形高度 【新年快乐】2024 年末一个人日本跨年之旅游记 【Cesium】实现上下垂直的卷帘分割效果 【单口喜剧】Louis C.K. 《Ridiculous》香港场线下观演小记
tRPC 配合 Vue 构建端到端类型安全的全栈方案简介
wumanho · 2026-04-16 · via OK Computer

tRPC 简介

tRPC 是一个基于 TypeScript 的全栈框架,相对其他全栈框架例如 next 等不算特别流行。

但随着 TypeScript 的应用越来越广泛, tRPC 的优势就越来越明显。

什么是 RPC

要了解 tRPC,就需要先理解什么是 RPC,RPC(Remote Procedure Call)即远程过程调用,通常被应用于后端分布式系统中。

RPC 与 HTTP 一样,都是一种服务端/客户端通讯的手段。

在 HTTP 中,我们通过请求一个 URL 来获取结果,而在 RPC 中,我们可以直接像调用本地程序一样直接调用一个函数,来获取这个函数的结果,这样的好处有很多,例如省去了数据序列化和反序列化的步骤,还有提高了安全性。

当然,RPC 调用也是基于网络请求的(可以是 http, tcp, udp 协议,tRPC 是基于 http 的),只是像 tRPC 这种框架会对调用过程进行封装,对用户来说是无感知的。

tRPC 的关键优势

如上文所说,RPC 可以使客户端直接调用服务端的函数,基于这一点,前后端不仅可以共享一套类型,实现端到端的类型安全,还可以利用更全面的智能类型推断来驱动生产力。

效果可以参考 tRPC 官网视频:

https://wumanhoblogimg.obs.cn-south-1.myhuaweicloud.com/images/tRPC/trpcdemo.gif

初始化 monorepo 项目

本次实验将会使用 pnpm 管理 monorepo ,先初始化项目:

在项目的跟目录创建工作空间配置文件pnpm-workspace.yaml

1
2
packages:
  - "packages/*"

所有代码文件都会放在 packages 目录下,初始化完成后项目结构如下:

1
2
3
4
5
6
7
8
9
├── node_modules
├── packages
│   ├── client
│   ├── server
├── pnpm-lock.yaml
├── .gitignore
├── .prettierrc
├── pnpm-workspace.yaml
├── package.json

安装 trpc 依赖:

1
pnpm install @trpc/client @trpc/server -w

搭建服务端

进入 server 目录

初始化 npm 项目,安装必要依赖:

1
2
npm init --name="@trpc-vue/server"
pnpm i filter zod -r --filter @trpc-vue/server

初始化 tRPC 服务端

1
2
3
4
5
6
// file: packages/server/trpc.ts

import { initTRPC } from '@trpc/server'
const t = initTRPC.create()
export const router = t.router
export const publicProcedure = t.procedure

数据库操作模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// file: packages/server/db.ts
export type User = { id: string; name: string }

const users: User[] = []
export const db = {
  user: {
    findMany: async () => users,
    findById: async (id: string) => users.find((user) => user.id === id),
    create: async (data: { name: string }) => {
      const user = { id: String(users.length + 1), ...data }
      users.push(user)
      console.log(users, 'usrs')
      return user
    }
  }
}

定义接口路由 & 启动服务

我们先对以下代码会用到的两个关于 tRPC 的关键概念做个简单介绍:

  • publicProcedure:Procedure(过程) 是 RPC 中的一个概念,是指在远程服务器上执行的特定功能或操作,可以理解为一个 API 端点,在 tRPC 中,一个过程可以有三种定义:query指一次查询操作,mutation指一次增删改操作,subscription指一次订阅,订阅模式本篇博客不作介绍。
  • Validation:校验,tRPC 支持对输入参数进行校验,以下代码使用zod库进行校验
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// file: packages/server/index.ts

import { router, publicProcedure } from './trpc'
import { createHTTPServer } from '@trpc/server/adapters/standalone'
import { db } from './db'
import { z } from 'zod'

/**
 *  接口路由定义
 */
const appRouter = router({
  userList: publicProcedure.query(async () => {
    const users = await db.user.findMany()
    return users
  }),
  userById: publicProcedure.input(z.string()).query(async (opts) => {
    const { input } = opts
    const user = await db.user.findById(input)
    return user
  }),
  userCreate: publicProcedure.input(z.object({ name: z.string() })).mutation(async (opts) => {
    const { input } = opts
    const user = await db.user.create(input)
    return user
  })
})

// 创建服务
const server = createHTTPServer({
  router: appRouter
})

// 注意这里导出的是类型,用于自动类型推导的
export type AppRouter = typeof appRouter

// 监听端口
server.listen(3721)

搭建客户端

进入 client 目录

1
2
3
cd ./packages/client
# 创建 vite 项目,选 typescript
pnpm create vite

安装一下依赖:

1
pnpm i @trpc-vue/server -r --filter @trpc-vue/client

别的就可以不用动了。

配置代理

转发配置一下,不然也会有跨域问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// file: packages/client/vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      '/trpc': {
        target: 'http://localhost:3721',
        changeOrigin: true,
        secure: false,
        rewrite: (path) => path.replace(/^\/trpc/, '')
      }
    }
  }
})

封装 useTRPC hook

方便起见,将客户端连接也封装在 hook 里面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// file: packages/client/src/hooks/useTRPC.ts

import { httpLink, createTRPCProxyClient } from '@trpc/client'
import { AnyRouter } from '@trpc/server'

/**
 *  url:服务端地址
 *  headers:允许添加请求头,如携带令牌
 */
export function useTRPC<Router extends AnyRouter>(
  url: string,
  headers?: Parameters<typeof httpLink>[0]['headers'],
  transformer?: Parameters<typeof createTRPCProxyClient>[0]['transformer']
) {
  const httpLinkConfig = httpLink({ url: url, headers })
	// 创建 trpc 客户端
  const trpc = createTRPCProxyClient<Router>({
    transformer,
    links: [httpLinkConfig]
  })
  return { trpc }
}

页面调用 trpc 服务端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script setup lang="ts">
// file: packages/client/src/App.vue
  
import { ref } from 'vue'
import type { User } from '@trpc-vue/server/types'
import { useTRPC } from './hooks/useTRPC'
  
// 这里导入服务端的路由定义
import { AppRouter } from '@trpc-vue/server'
const { trpc } = useTRPC<AppRouter>('/trpc')

// 新增用户
const addUser = async () => {
  await trpc.userCreate.mutate({
    name: new Date().toISOString() + '用户'
  })
  await initPage()
}

const users = ref<User[]>([])

// 查询用户
async function initPage() {
  users.value = await trpc.userList.query()
}
initPage()
</script>

<template>
  <ul>
    <li v-for="item in users" :key="item.id">{{ item.name }}</li>
  </ul>
  <button @click="addUser">添加用户</button>
</template>

<style lang="scss" scoped></style>

测试

启动服务端:

1
2
3
4
cd ./packages/server/

# 用 ts-node 执行 index.ts
ts-node ./index.ts

启动客户端:

1
2
3
cd ./packages/client/

npm run dev

新增和查询:

https://wumanhoblogimg.obs.cn-south-1.myhuaweicloud.com/images/tRPC/trpc-add-query.gif

新增和查询调用成功,测试完成。

总结

完整代码仓库

虽然本次实验是比较简单的,但已经可以充分展现出 tRPC 强大的地方。

实际上,还有更强大的类型推导这一点在博客中无法展示,tRPC 是一个完全可以在生产环境应用的成熟的库,推荐各位有兴趣的读者亲自动手试一试。

(完)