Vue3 + TS + Vue Query + Express + tRPC:设置示例
目录
介绍
最近我在谷歌上搜索有关 Web 开发趋势的信息,以便了解最新的工具/库/框架,然后偶然发现了 tRPC。
tRPC 代表TypeScript 远程过程调用,正如其官网所述,它的目的是轻松实现端到端的类型安全 API。本质上,它允许您使用 TypeScript 的所有强大功能,公开可从客户端(前端)调用的服务器函数。
tRPC 是确保客户端和服务器之间(通过 API 调用)正确通信的另一种方式。您可能已经考虑使用 GraphQL 来实现这一点,但使用 tRPC 无需学习新的语言,它本身也不是一个模式。而 GraphQL 既是一个模式,也是一种语言,它用于详细描述您可以从服务器调用的函数的“结构”。
实验:何不尝试使用最新版本的Vue、Vite和TypeScript,并尝试集成tRPC,看看效果如何?
我搜索了一些基于 Vue 并使用 tRPC 的项目,但结果绝大多数都是基于 React/Next.js 的……所以我决定先从一个基于 React 的项目开始,然后再进行其他实验。
注:
- 我会在文章中提供所有相关资源的链接
。- 这只是一个实验性的想法,旨在集成几个现代软件包并创建一个非常简单的项目
。- 本文更适合已有一定 Web 开发经验的读者,但我会尽量提供一些额外的解释。
设置
首先,我观看了Jack Herrington关于“ tRPC:智能简易 API ”的精彩视频,并按照他的步骤操作,然后想知道使用 Vue 3 和Vue Query分别代替 React 和 React Query会有多难。
下一节展示了根据 Jack 的步骤并修改为使用 Vue 后最终的文件夹结构。
项目文件夹结构
这是一个使用 Yarn 工作区的单体仓库。
服务器项目位于api-server文件夹中,前端项目位于client文件夹中。
服务器和客户端都是在根目录下启动的yarn start,正如您在根文件夹下的 package.json 文件中看到的那样:"start": "concurrently \"wsrun --parallel start\""
服务器脚本
这是服务器代码,我们在这里创建了我们的 express 应用程序,并告诉它使用 cors(允许从端口 3000 到 8080 的调用),还使用 trpcExpress 中间件并注册路由器。
// packages\api-server\index.ts
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router/app';
import cors from 'cors';
const main = async () => {
const app = express();
app.use(cors());
const port = 8080;
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext: () => null,
})
);
app.listen(port, () => {
console.log(`api-server listening at http://localhost:${port}`);
});
};
main();
路由器
以下代码显示了包含接入点的路由器:
- 2 个查询端点(类似于 REST GET 端点):
- 问候
- 获取消息
- 1 个突变端点(类似于 REST POST 端点):
- 添加消息
注意:除了添加数据外,变异还可以更新或删除数据。
您还可以看到我正在使用zod,这是一个“TypeScript 优先的模式声明和验证库”。
该软件包将用于验证我的查询/变更输入(如有必要,这些验证甚至可以抛出验证消息)。
z.string().uuid({ message: "Invalid UUID" });
注意:您还可以使用 zod 从 zod 对象推断类型,将它们存储为类型并在任何地方重用它们:
// packages\api-server\router\app.ts
import * as trpc from '@trpc/server';
import { z } from 'zod';
import { v4 as uuidv4 } from 'uuid';
export interface ChatMessage {
id: string;
user: string;
message: string;
}
const messages: ChatMessage[] = [
{ id: uuidv4(), user: 'User1', message: 'This is my the first message!' },
{ id: uuidv4(), user: 'User2', message: 'Hello there 🎉' },
];
export const appRouter = trpc
.router()
.query('greetings', {
resolve() {
return {
message: 'Greetings from /trpc/greetings:)',
};
},
})
.query('getMessages', {
input: z.number().default(10),
resolve({ input }) {
return messages.slice(-input);
},
})
.mutation('addMessage', {
input: z.object({
user: z.string(),
message: z.string(),
}),
resolve({ input }) {
const newMessage: ChatMessage = {
id: uuidv4(),
...input,
};
messages.push(newMessage);
return input;
},
});
export type AppRouter = typeof appRouter;
在这种情况下,消息只会存储在内存中,因为我没有使用数据库。(这样演示起来也更快)。
还可以创建不同的路由器,每个路由器包含不同的查询/变更操作,然后合并这些路由器,以便在客户端轻松地从某个路由器访问特定的查询。
Vue 查询初始化
这是通过 VueQueryPlugin 在 main.ts 文件中初始化 vue-query 的方法,然后 Vue 应用程序实例会使用它:
// packages\client\src\main.ts
import { createApp } from 'vue';
import { VueQueryPlugin } from 'vue-query';
import './style.css';
import App from './App.vue';
createApp(App).use(VueQueryPlugin).mount('#app');
你可能会问,为什么一开始要使用 Vue Query 呢?
“我完全可以用 fetch/axios 来完成所有的 API 调用,对吧?”
没错,不过这个软件包确实提供了一些开箱即用的实用功能,例如缓存、重试、重新获取、无限查询(用于无限滚动)等等。以下是随着项目复杂性增加,可能会出现的一些挑战(摘自官方文档):
- 缓存……(可能是编程中最难的事情之一)
- 将对同一数据的多个请求去重,合并成单个请求。
- 在后台更新“过期”数据
- 了解数据何时“过时”
- 尽快反映数据更新
- 性能优化,例如分页和延迟加载数据
- 管理内存和服务器状态的垃圾回收
- 使用结构共享记忆查询结果
这些钩子提供了一组标准属性/函数,供您在应用程序中使用。例如,useQuery 钩子:注意:您需要访问的数据位于名为data 的属性中。
tRPC 客户端
这里我们定义了 tRPC 客户端调用中需要使用的 URL,以及可以使用的类型(来自 AppRouter)。(稍后我们将在 App.vue 组件中导入这个 trpc 常量):
// packages\client\src\api\trpc.ts
import { createTRPCClient } from '@trpc/client';
import { AppRouter } from 'api-server/router/app';
export const trpc = createTRPCClient<AppRouter>({
url: 'http://localhost:8080/trpc',
});
应用程序组件
为了简单起见,我决定在这个组件中执行 tRPC 客户端调用。
注:我使用的是 Vue 的脚本配置,目前为止使用体验很棒 :)
<template>
<div class="trpc-example">
<h1>Vue 3 + vue-query + tRPC example</h1>
<Error
v-if="getMessagesHasError"
error-message="Something went wrong - cannot fetch data"
cta-text="Refetch data"
@click="refetch()"
/>
<Error
v-if="addMessageHasError"
error-message="Something went wrong - cannot submit message"
cta-text="Reset error"
@click="reset"
/>
<div v-if="showFormAndMessages" class="trpc-example__container">
<SendMessageForm :form="form" @submit-form="handleSubmitForm" />
<h2 v-if="isLoading">Data is being loaded</h2>
<Message v-for="chatMessage in data" :key="chatMessage.id" :chat-message="chatMessage" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed, reactive } from 'vue';
import Message from './components/Message.vue';
import SendMessageForm from './components/SendMessageForm.vue';
import Error from './components/Error.vue';
import { useQuery, useMutation, useQueryClient } from 'vue-query';
import { trpc } from './api/trpc';
import { Form } from '../types';
const queryClient = useQueryClient();
const form = reactive({
user: '',
message: '',
});
const getMessages = () => trpc.query('getMessages');
const {
isError: getMessagesHasError,
isLoading,
data,
refetch,
} = useQuery('getMessages', getMessages, {
refetchOnWindowFocus: false,
});
const addMessage = (form: Form) => trpc.mutation('addMessage', form);
const { error: addMessageHasError, mutate, reset } = useMutation('addMessage', addMessage);
const handleSubmitForm = () => {
mutate(form, {
onSuccess: () => {
queryClient.invalidateQueries('getMessages');
},
});
};
const showFormAndMessages = computed(() => {
return !getMessagesHasError.value && !addMessageHasError.value;
});
</script>
应用及示例
显然,与此项目互动最好的方式是在本地运行它,看看你能用它做什么。以下是一些示例:
这就是客户端的样子(是的,我知道,用户界面看起来很棒!)。Vue.js 开发者工具还会显示查询信息:
以下示例展示了如何更改服务器端功能并在客户端观察 TS 安全检查:

您也可以从客户端重命名服务器函数(由于某些原因,我无法从服务器端重命名符号):
以下示例演示了如何阻止变更请求,然后调用重置函数。这将重置错误状态:
更多实用链接
- 我的仓库:https://github.com/alousilva/express-vue-trpc
- tRPC 的创建者 Alex:https://twitter.com/alexdotjs
- Theo - ping․gg,Alex 的采访:https://www.youtube.com/watch? v=Mm3Z5c1Linw (顺便说一句,Theo 的 YouTube 频道上有很多有趣的内容)
- 和杰森一起学习,采访亚历克斯:https://www.youtube.com/watch?v =GryES84SSEU
我可能会创建另一个仓库,探索一个更实际的项目,使用 Nuxt、tRPC 和 Vue Query,并连接数据库和使用 ORM Prisma,类似于 Alex 在这个非常棒的入门仓库中所做的:https://github.com/trpc/examples-next-prisma-starter
希望这篇文章对您有所帮助,也希望它能让您今天有所收获 :)
文章来源:https://dev.to/alousilva/vue3-typescript-express-trpc-setup-example-2mlh



