- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
导读
@midwayjs/vue是 Midway 框架为 Vue 3 前端提供的一套“函数式 API 桥接”集成包,它让 Vue 应用可以直接复用后端 Midway 项目中通过defineApi声明的 API 定义,以api.user.getUser({ params: { id } })这样类型安全、语义化的方式发起请求。本文基于该包源码与仓库内可运行的 samples/vue-functional-api 示例,讲解从安装、注册插件到组合式调用、Provider 注入的完整链路,并深入src/index.ts揭示其基于 Vue 3provide/inject的底层实现原理。
背景:什么是 Midway 函数式 API 桥接
Midway 的函数式 API(Functional API)允许开发者用defineApi在服务端集中声明路由,并自动生成可供前端直接调用的“API 客户端”结构。@midwayjs/vue与@midwayjs/react、@midwayjs/api-bridge、@midwayjs/web-bridge同属于这一桥接家族:
@midwayjs/web-bridge是运行时依赖,负责提供createClient等核心客户端能力(其 src/index.ts 直接export * from '@midwayjs/api-bridge');@midwayjs/vue在其之上补齐 Vue 专属的插件与组合式 API,让 Vue 组件可以用最“Vue 化”的方式消费客户端。
从 package.json 可以看到,@midwayjs/vue将@midwayjs/web-bridge作为依赖,并将vue声明为peerDependencies(要求vue >= 3),因此它天然面向 Vue 3 组合式 API 生态。
安装与依赖
npm i @midwayjs/vue @midwayjs/web-bridge对应的package.json依赖声明为:
{ "dependencies": { "@midwayjs/vue": "^4.0.0-beta.11", "@midwayjs/web-bridge": "^4.0.0-beta.11" } }要点说明:
vue无需显式安装为依赖,但运行时版本需满足>= 3(peerDependencies 约束),否则插件注入与组合式函数将不可用;@midwayjs/web-bridge提供客户端核心(createClient),@midwayjs/vue提供 Vue 集成层,二者需成对安装;- 当前仓库中该包版本为
4.2.3,engines要求node >= 20,构建产物同时提供 CommonJS(dist/index.js)与 ESM(dist/index.mjs)双格式,并通过exports字段区分require与import入口(见 packages/vue/package.json)。
基本用法:插件注册 + 组合式调用
1. 创建 Vue 应用并注册插件
import { createApp } from 'vue'; import { createMidwayApiPlugin } from '@midwayjs/vue'; import { api } from './api/client'; import App from './App.vue'; createApp(App).use(createMidwayApiPlugin(api)).mount('#root');createMidwayApiPlugin(client)返回一个标准的 Vue 3 插件对象(Plugin),其内部通过app.provide(InjectionKey, client)将客户端实例注入到整个应用上下文(见 src/index.ts),此后任意组件树中都能取到同一个客户端实例。
2. 在组件中调用 API
import { useMidwayApiOperation } from '@midwayjs/vue'; const callGetUser = useMidwayApiOperation<{ params: { id: string } }, { id: string }>('user.getUser'); await callGetUser({ params: { id: '1' } });useMidwayApiOperation是一个组合式函数(Composable),它接收operationId(如'user.getUser',格式为模块名.路由名),返回一个绑定好该操作 ID 的调用函数。泛型参数<TInput, TOutput>分别约束入参与返回值类型,从而在编译期获得完整的类型提示。
客户端创建:从服务端 API 定义到api.user.getUser
配合仓库内的可运行示例 samples/vue-functional-api,完整的链路如下。
服务端:声明 API 定义
// samples/vue-functional-api/src/server/api/user.api.ts import { defineApi } from '@midwayjs/core/functional'; export const userApi = defineApi('/users', api => ({ getUser: api .get('/:id') .meta({ routerName: 'getUser' }) .handle(async ({ input }) => { return { id: input.params?.['id'], name: 'harry', }; }), }));前端:创建命名空间客户端
// samples/vue-functional-api/src/web/api/client.ts import { createClient } from '@midwayjs/vue'; import { userApi } from '../../server/api/user.api.js'; export const api = createClient( { user: userApi, }, { basePath: { browser: '/api', server: 'http://127.0.0.1:7001/api', }, } );createClient的键(user)会成为调用前缀,最终形成api.user.getUser(...)的链式调用风格。basePath支持browser/server双配置,便于同构(CSR/SSR)场景下区分浏览器相对路径与服务器绝对路径。
核心链路:createClient 如何工作
测试用例 test/index.test.ts 精确验证了这一行为:传入带__midwayApiMeta.prefix与路由元信息的 API 定义后,api.user.getUser({ params: { id: 'u-1' } })会按get + '/users/:id'解析并调用底层的adapter,最终返回{ id: 'u-1' }。也就是说,createClient负责把声明式的路由元数据(method、path、routerName)转换为真实请求,并通过可替换的adapter决定实际网络发送方式。
核心 API 与底层实现(源码级解析)
@midwayjs/vue的全部导出集中在 src/index.ts,除createClient(来自 web-bridge)外共提供 4 个 Vue 专属能力,其实现全部建立在 Vue 3 的依赖注入机制之上。
客户端类型约定
interface MidwayApiClientLike { call(operationId: string, input: unknown): Promise<unknown>; has(operationId: string): Promise<boolean> | boolean; operationIds(): Promise<string[]> | string[]; }这是桥接层的“最小客户端契约”:call执行调用、has判断某操作是否存在、operationIds枚举全部操作 ID。只要对象满足该形状(例如由createClient生成的真实客户端,或测试中的 mock),即可被注入与消费。
注入键与 Provider 组件
const MidwayApiClientInjectionKey: InjectionKey<MidwayApiClientLike> = Symbol('MidwayApiClient');- 注入键是
Symbol('MidwayApiClient'),配合 TypeScript 的InjectionKey<T>泛型,保证注入/取用时类型一致; - 除插件方式外,包内还提供了声明式组件
MidwayApiProvider(defineComponent),通过props.client接收客户端并在setup中provide给子树,适合“局部注入”而非全局注册的场景(见 src/index.ts)。
插件与组合式函数
export function createMidwayApiPlugin(client: MidwayApiClientLike): Plugin { return { install(app) { app.provide(MidwayApiClientInjectionKey, client); }, }; }插件本质是对app.provide的一次封装;而组合式函数则通过inject反向获取:
export function useMidwayApiClient<TInput = unknown, TOutput = unknown>() { const client = inject(MidwayApiClientInjectionKey, null); if (!client) { throw new Error( 'useMidwayApiClient must be used inside app.use(createMidwayApiPlugin(client)) or <MidwayApiProvider>' ); } // ... } export function useMidwayApiOperation<TInput = unknown, TOutput = unknown>( operationId: string ) { const client = useMidwayApiClient<TInput, TOutput>(); return (input: TInput) => client.call(operationId, input); }由此形成清晰的依赖链:
createApp(App).use(createMidwayApiPlugin(api)) // 全局注入 ↓ provide(Symbol('MidwayApiClient'), api) useMidwayApiClient() // inject 取回客户端 ↓ useMidwayApiOperation('user.getUser') // 绑定 operationId ↓ await callGetUser({ params: { id: '1' } }) // client.call('user.getUser', input)错误保护:未注入时的显式报错
useMidwayApiClient在取不到客户端时抛出异常,提示必须通过app.use(createMidwayApiPlugin(client))或<MidwayApiProvider>注入。对应测试 test/index.test.ts 明确断言了该错误信息,帮助开发者快速定位“组合式函数用在了 Provider 之外”的常见误用。
测试验证与 ESM 产物检查
仓库为该包提供了两层测试保障:
- 单元测试(test/index.test.ts):覆盖客户端命名空间调用、插件
install的provide调用次数与注入键(expect.any(Symbol))、未注入时报错三组关键行为; - ESM 产物冒烟测试(test/esm-dist.test.mjs):直接
import构建产物dist/index.mjs,断言createClient、createMidwayApiPlugin、MidwayApiProvider、useMidwayApiClient、useMidwayApiOperation均已正确导出且可运行,并使用自定义adapter验证createClient的operationId与input传递。
这一“单测 + dist 冒烟”的组合,既验证了源码逻辑,也保证了发布后双格式产物(CJS/ESM)的可用性。
仓库内可运行的完整示例
若希望端到端体验,仓库内置了 Vue + Midway 函数式 API 的可运行示例 samples/vue-functional-api:
# 在仓库根目录 pnpm install # 启动:内置 Vite 开发服务器 + 内嵌 Midway HTTP 运行时 pnpm -C samples/vue-functional-api dev该示例的关键配置:
src/server/api/user.api.ts:用defineApi('/users', ...)声明getUser、createUser两个操作;src/web/api/client.ts:createClient({ user: userApi }),并配置浏览器端/api、服务端http://127.0.0.1:7001/api双 basePath;src/main.ts:createApp(App).use(createMidwayApiPlugin(api)).mount('#root'),与本文核心用法完全一致;- 后端为真实 Midway Koa 应用(
imports: [koa]),/api/*请求由真实路由处理; - 构建时可通过
pnpm -C samples/vue-functional-api build:server与build:web拆分产出,分别输出dist/server与dist/web。
小结
@midwayjs/vue将 Midway 的函数式 API 桥接能力“翻译”成了 Vue 3 开发者熟悉的表达方式:
- 插件式注册:
createMidwayApiPlugin(api)全局注入客户端,一行代码完成初始化; - 组合式调用:
useMidwayApiOperation/useMidwayApiClient让组件内调用与 Vue 响应式生命周期自然融合; - Provider 局部注入:
MidwayApiProvider提供按子树注入的灵活性; - 类型安全:
InjectionKey+ 泛型约束贯穿注入、取用与调用全过程。
其底层实现极简而可靠:一个Symbol注入键 +provide/inject机制 + 最小客户端契约(call/has/operationIds)。理解这层实现后,无论是接入现有 Vue 项目、排查“组合式函数未注入”报错,还是基于同一契约实现自定义桥接层,都会更加得心应手。
- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
相关推荐
Windows系统优化工具终极指南:5分钟完成2小时的手动优化
Windows系统优化工具终极指南:5分钟完成2小时的手动优化 你是否厌倦了Windows系统缓慢、预装软件过多、隐私设置复杂的问题?面对分散的系统管理工具和繁
桌面应用运维Vue Native中的组合式API:setup函数与Composition API
Vue Native中的组合式API:setup函数与Composition API 在移动应用开发中,随着项目复杂度提升,传统Options API(选项式A
移动开发跨平台前端gh_mirrors/vit/vitesse中的Vue 3组合式API实践
gh_mirrors/vit/vitesse中的Vue 3组合式API实践 组合式API简介 组合式API(Composition API)是Vue 3引入的新
前端示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考