Nuxt中使用TypeScript装饰器报错的解决方法咨询
Nuxt中使用TypeScript装饰器出现语法错误的解决方法
背景
我在Nuxt应用的/service目录下创建服务层,通过类(比如LocationService)管理后端API调用(查询、创建、删除地点)。为了使用这些服务,我给它们提供store、useNotification或useToast等上下文对象,封装成composable。
为了让代码更整洁有序,我尝试添加装饰器来为函数扩展额外功能。
示例代码
比如我想为每个API调用添加加载状态:
import {ToastService} from "./useToastService"; import {WithLoading} from "./with-loading.decorator"; import {RestClient} from "@ayu-sh-kr/dota-rest"; export class LocationService { restClient!: RestClient; constructor(public toast: ToastService) { } @WithLoading() async get() { return await this.restClient.get<Location[]>() .uri('/location/v1') .retrieve() .toEntity(); } }
目前@WithLoading装饰器还没实现功能,只是打算通过LoadingStore管理加载状态,但使用后出现500错误及未识别令牌报错。
错误信息
@__vite_ssr_import_2__.WithLoading() ^ SyntaxError: Invalid or unexpected token at Script (node:vm:116:7) at createScript (node:vm:268:10) at Object.runInThisContext (node:vm:316:10) at ViteNodeRunner.runModule (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:398:19) at ViteNodeRunner.directRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:381:16) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async ViteNodeRunner.cachedRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:206:14) at async ViteNodeRunner.dependencyRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:259:12) at async components\route\location\LocationTable.vue:9:31 at async ViteNodeRunner.runModule (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:399:5)
问题在于,尽管我已经在tsconfig.json中启用了装饰器支持,Nuxt仍然无法处理装饰器。
当前配置
tsconfig.json
{ // https://nuxt.com/docs/guide/concepts/typescript "extends": "./.nuxt/tsconfig.json", "compilerOptions": { "experimentalDecorators": true, // Enable decorator support "emitDecoratorMetadata": true, // Required for metadata reflection with decorators "target": "esnext", // Use "esnext" or a compatible ES version "module": "esnext" // Ensure modern module support } }
nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ compatibilityDate: '2024-11-01', devtools: {enabled: true}, css: ['~/assets/css/main.css'], postcss: { plugins: { tailwindcss: {}, autoprefixer: {}, }, }, ssr: true, modules: ['@nuxt/ui', '@pinia/nuxt'], runtimeConfig: { public: { BACKEND_URL: process.env.NUXT_PUBLIC_BACKEND_URL } } })
解决方法
- 安装必要的Vite插件
Nuxt默认编译链需要额外插件处理装饰器语法,安装@vitejs/plugin-react(即使使用Vue,该插件也能兼容处理TypeScript装饰器):
pnpm add -D @vitejs/plugin-react
- 配置Nuxt的Vite插件
修改nuxt.config.ts,添加Vite插件及装饰器相关Babel配置:
import react from '@vitejs/plugin-react' export default defineNuxtConfig({ // ...其他原有配置 vite: { plugins: [ react({ babel: { plugins: [ ['@babel/plugin-proposal-decorators', { legacy: true }], 'babel-plugin-transform-typescript-metadata' ] } }) ] } })
这里legacy: true需要和tsconfig.json中的experimentalDecorators配置匹配,确保语法兼容。
- 检查装饰器实现规范
确保WithLoading装饰器符合TypeScript函数式装饰器的写法,示例实现如下:
// with-loading.decorator.ts import { useLoadingStore } from '~/stores/loading' export function WithLoading() { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value descriptor.value = async function (...args: any[]) { const loadingStore = useLoadingStore() loadingStore.startLoading(propertyKey) try { return await originalMethod.apply(this, args) } finally { loadingStore.stopLoading(propertyKey) } } return descriptor } }
重启开发服务
修改配置后必须重启Nuxt开发服务器,确保新配置生效。备选方案:改用组合式函数替代装饰器
如果装饰器配置仍有问题,可以直接用组合式函数包裹API调用,绕开编译问题:
// useLoadingWrapper.ts import { useLoadingStore } from '~/stores/loading' export async function withLoading<T>(key: string, fn: () => Promise<T>): Promise<T> { const loadingStore = useLoadingStore() loadingStore.startLoading(key) try { return await fn() } finally { loadingStore.stopLoading(key) } }
在服务类中使用:
async get() { return await withLoading('location:get', () => this.restClient.get<Location[]>() .uri('/location/v1') .retrieve() .toEntity() ) }
内容的提问来源于stack exchange,提问作者Ayush Jaiswal
相关产品推荐
相关产品推荐

