Vue 3 TSX 与 Template 语法对比:5个核心指令的差异分析与迁移策略
2026/7/11 22:07:36 网站建设 项目流程

Vue 3 TSX与Template语法深度对比:5大核心指令的工程化实践

从模板到TSX:现代Vue开发的范式迁移

在Vue 3的生态系统中,我们正见证着一种有趣的范式转变——越来越多的开发者开始尝试在Vue中使用TSX语法。这种转变并非偶然,TypeScript的静态类型检查与JSX的表达力相结合,为大型应用开发带来了全新的可能性。不同于传统的模板语法,TSX允许我们在同一个文件中自由混合JavaScript逻辑与标记语言,这种开发体验对于React背景的开发者尤其亲切。

环境配置要点

  • 必须安装@vitejs/plugin-vue-jsx插件
  • tsconfig.json需要特殊配置:
    { "compilerOptions": { "jsx": "preserve", "jsxFactory": "h", "jsxFragmentFactory": "Fragment" } }
  • Vite配置示例:
    import vueJsx from '@vitejs/plugin-vue-jsx' export default defineConfig({ plugins: [vue(), vueJsx()] })

在实际项目中,我们常常会遇到这样的场景:一个复杂的表单组件在模板语法中需要大量v-ifv-for嵌套,导致可读性急剧下降。这时转换为TSX可能会获得更清晰的代码结构。但要注意,这种转换不是简单的语法替换,而是思维模式的转变。

核心指令的语法对照

条件渲染的范式转换

在模板语法中,我们习惯使用v-ifv-show来控制元素显示。但在TSX中,情况有所不同:

指令Template语法TSX实现方式
v-show<div v-show="flag"/><div v-show={flag.value}/>
v-if<div v-if="flag"/>{flag.value && <div/>}

关键差异

  • TSX中没有真正的v-if指令,需要改用JavaScript条件表达式
  • v-show虽然可用,但要注意.value的解包
  • 复杂条件建议提取为独立函数
// TSX中的条件渲染最佳实践 const renderContent = () => { if (user.role === 'admin') { return <AdminPanel /> } else if (user.role === 'editor') { return <EditorTools /> } return <GuestView /> }

列表渲染的思维转换

v-for是Vue模板中最常用的指令之一,但在TSX中需要完全改变思路:

<!-- Template方式 --> <ul> <li v-for="item in items" :key="item.id"> {{ item.name }} </li> </ul>
// TSX方式 const items = ref([...]) return () => ( <ul> {items.value.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> )

性能注意事项

  • 大型列表应使用虚拟滚动技术
  • 避免在渲染函数中进行复杂计算
  • key的分配策略需要保持一致

双向绑定与事件处理

v-model的TSX实现

Vue 3的v-model在TSX中有多种表达方式:

// 基础用法 <input v-model={value.value} /> // 带参数和修饰符 <input v-model={[value.value, 'modelValue', ['trim']]} /> // 多v-model绑定 <Modal v-model={visible.value} v-model:title={title.value} />

类型安全增强

interface FormState { username: string password: string } const form = ref<FormState>({ username: '', password: '' }) // 现在IDE能提供自动补全和类型检查 <input v-model={form.value.username} />

事件处理的差异

事件绑定是TSX与Template差异较大的领域:

特性Template语法TSX语法
事件前缀@on
命名规范kebab-casecamelCase
修饰符.stop.preventwithModifiers
// 基础事件 <button onClick={handleClick}>点击</button> // 带修饰符的事件 <button onClick={withModifiers(handleClick, ['stop'])}> 阻止冒泡 </button> // 类型安全的事件处理器 const handleSubmit = (e: MouseEvent) => { e.preventDefault() // 提交逻辑 }

Props与组件通信

类型安全的Props定义

TSX的最大优势在于能实现完全类型安全的组件接口:

interface ButtonProps { type?: 'primary' | 'danger' | 'default' size?: 'large' | 'medium' | 'small' loading?: boolean onClick?: (e: MouseEvent) => void } const Button = defineComponent({ setup(props: ButtonProps) { return () => ( <button class={[ 'btn', `btn-${props.type || 'default'}`, `btn-${props.size || 'medium'}` ]} disabled={props.loading} onClick={props.onClick} > {props.loading ? <LoadingIcon /> : slots.default?.()} </button> ) } })

插槽的类型化实现

TSX中的插槽系统需要适应React风格的children模式:

// 定义带插槽的组件 const Card = defineComponent({ setup(props, { slots }) { return () => ( <div class="card"> {slots.header?.() || <DefaultHeader />} <div class="content"> {slots.default?.()} </div> {slots.footer && ( <div class="footer"> {slots.footer()} </div> )} </div> ) } }) // 使用组件 <Card v-slots={{ header: () => <h2>自定义标题</h2>, default: () => <p>内容区</p>, footer: () => <button>确认</button> }} />

渐进式迁移策略

阶段一:混合开发模式

在现有项目中逐步引入TSX的最佳实践:

  1. 从工具函数组件开始尝试
  2. 在.vue文件中使用<script lang="tsx">
  3. 配置好TypeScript和ESLint支持
<!-- HybridComponent.vue --> <template> <!-- 保留现有模板 --> <div>{{ count }}</div> </template> <script lang="tsx"> // 新增TSX逻辑 const renderButton = () => ( <button onClick={increment}>TSX按钮</button> ) export default defineComponent({ setup() { const count = ref(0) const increment = () => count.value++ return { count, increment, renderButton } } }) </script>

阶段二:构建TSX组件库

创建可复用的TSX基础组件:

// components/TSXButton.tsx import { defineComponent } from 'vue' interface Props { type?: string onClick?: (e: MouseEvent) => void } export default defineComponent({ props: { type: String, onClick: Function }, setup(props: Props) { return () => ( <button class={`btn-${props.type}`} onClick={props.onClick} > {slots.default?.()} </button> ) } })

阶段三:全量TSX改造

当团队熟悉TSX后,可以考虑全面迁移:

  1. 建立代码规范标准
  2. 开发自定义Vite插件处理TSX转换
  3. 配置静态类型检查流水线

迁移检查清单

  • [ ] 所有.value访问都已正确处理
  • [ ] 事件处理器已转换为onXxx格式
  • [ ] 条件渲染已重构为逻辑表达式
  • [ ] 列表渲染已替换为.map调用
  • [ ] 自定义指令已适配JSX环境
// 全TSX组件示例 const UserProfile = defineComponent({ setup() { const user = useUserStore() return () => ( <div class="profile"> <Avatar src={user.avatar} size="large" /> <h2>{user.name}</h2> <div class="stats"> {user.stats.map(stat => ( <StatItem key={stat.id} label={stat.label} value={stat.value} /> ))} </div> {user.isAdmin && <AdminControls />} </div> ) } })

工程化实践与性能优化

类型安全的项目架构

大型Vue3+TSX项目推荐结构:

src/ ├── components/ # 通用TSX组件 │ ├── ui/ # 无状态UI组件 │ └── business/ # 业务组件 ├── composables/ # 组合式函数 ├── stores/ # 状态管理 ├── utils/ # 工具函数 └── views/ # 页面级TSX组件

渲染性能优化技巧

  1. 记忆化组件

    const HeavyComponent = memo(defineComponent({ setup(props) { // 复杂计算 return () => <div>{/*...*/}</div> } }))
  2. 细粒度响应式

    const list = ref(largeDataSet) // 坏实践 - 整个列表响应式 // 好实践 - 仅当前页数据响应式 const visibleItems = computed(() => { return list.value.slice(currentPage * pageSize, (currentPage + 1) * pageSize) })
  3. 虚拟滚动集成

    <VirtualList items={items.value} itemHeight={50} renderItem={(item) => <ListItem item={item} />} />

调试与错误处理

TSX环境特有的调试技巧:

  1. 类型断言调试

    console.log('props:', props as unknown as MyDebugType)
  2. JSX源映射配置

    // vite.config.js export default defineConfig({ plugins: [ vueJsx({ transformOn: true, optimize: true, enableObjectSlots: false }) ] })
  3. 自定义Babel插件

    // 可用于添加调试信息 module.exports = function() { return { visitor: { JSXElement(path) { // 添加调试属性 } } } }

生态整合与高级模式

与Vue生态的兼容性

  1. Vue Router集成

    const router = useRouter() const navigate = () => router.push('/detail') return () => ( <button onClick={navigate}> 跳转 </button> )
  2. Pinia状态管理

    const store = useUserStore() return () => ( <div> <span>{store.userName}</span> <button onClick={store.logout}>退出</button> </div> )
  3. 第三方组件库适配

    // 适配Element Plus <ElButton type="primary" onClick={handleClick} > 按钮 </ElButton>

高阶组件模式

  1. Render Props模式

    const DataFetcher = defineComponent({ props: { render: Function as PropType<(data: any) => JSX.Element> }, setup(props) { const data = fetchData() return () => props.render(data) } })
  2. 依赖注入模式

    const ThemeContext = createContext('light') const App = () => { return ( <ThemeContext.Provider value="dark"> <ChildComponent /> </ThemeContext.Provider> ) }
  3. 动态组件加载

    const AsyncComponent = defineAsyncComponent(() => import('./HeavyComponent.tsx') ) return () => ( <Suspense fallback={<Loading />}> <AsyncComponent /> </Suspense> )

样式管理与主题方案

CSS-in-JS实践

在TSX环境中管理样式的几种方案:

  1. Styled Components模式

    const StyledButton = styled('button', { padding: '8px 16px', borderRadius: '4px', variants: { variant: { primary: { backgroundColor: '#1890ff' } } } })
  2. CSS Modules集成

    import styles from './Button.module.css' const Button = () => ( <button class={styles.primary}> 按钮 </button> )
  3. 原子化CSS方案

    const Card = () => ( <div class="p-4 border rounded-lg shadow-sm"> <h3 class="text-xl font-semibold">标题</h3> </div> )

主题切换实现

类型安全的主题管理系统:

// theme.ts interface Theme { colors: { primary: string secondary: string } spacing: (factor: number) => string } export const lightTheme: Theme = { colors: { primary: '#1890ff', secondary: '#52c41a' }, spacing: (n) => `${n * 4}px` } // ThemeProvider.tsx const ThemeContext = createContext<Theme>(lightTheme) export const useTheme = () => useContext(ThemeContext) export const ThemeProvider: FC<{ theme: Theme }> = (props) => { return ( <ThemeContext.Provider value={props.theme}> {props.children} </ThemeContext.Provider> ) } // 使用示例 const ThemedButton = () => { const theme = useTheme() return ( <button style={{ backgroundColor: theme.colors.primary, padding: theme.spacing(2) }}> 主题按钮 </button> ) }

测试策略与质量保障

单元测试配置

TSX组件的测试配置方案:

// Button.spec.tsx import { render } from '@testing-library/vue' import Button from './Button.tsx' describe('Button组件', () => { it('应该渲染正确的内容', () => { const { getByText } = render(Button, { slots: { default: '点击我' } }) expect(getByText('点击我')).toBeInTheDocument() }) it('应该触发点击事件', async () => { const handleClick = vi.fn() const { getByRole } = render(Button, { props: { onClick: handleClick } }) await fireEvent.click(getByRole('button')) expect(handleClick).toHaveBeenCalled() }) })

类型测试策略

确保类型定义的正确性:

// Button.test-d.ts import type { MountingOptions } from '@vue/test-utils' import type { ButtonProps } from './Button' describe('Button类型测试', () => { it('应该接受有效的props', () => { const options: MountingOptions<ButtonProps> = { props: { type: 'primary', size: 'large' // @ts-expect-error 测试无效prop invalidProp: true } } expect(options).toBeDefined() }) })

E2E测试集成

使用Cypress进行组件测试:

// button.cy.tsx import Button from './Button.tsx' describe('Button组件交互', () => { it('应该响应点击事件', () => { const onClick = cy.stub() cy.mount(() => <Button onClick={onClick} />) cy.get('button').click().then(() => { expect(onClick).to.have.been.calledOnce }) }) })

构建优化与部署

代码分割策略

TSX项目的优化构建配置:

// vite.config.ts export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('tsx')) { return 'tsx-components' } } } } } })

Tree-shaking优化

确保TSX组件的可摇树性:

// 导出方式影响tree-shaking // 推荐方式 - 具名导出 export const Button = defineComponent({/*...*/}) // 不推荐方式 - 默认导出 export default defineComponent({/*...*/})

类型声明生成

自动生成d.ts文件配置:

// tsconfig.json { "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, "outDir": "dist/types" }, "include": ["src/**/*.ts", "src/**/*.tsx"] }

团队协作规范

代码风格指南

TSX项目特有的规范建议:

  1. 命名约定

    • 组件文件使用PascalCase命名(如Button.tsx
    • 属性类型使用Props后缀(如ButtonProps
  2. 代码组织

    // 推荐结构 const Component = defineComponent({ // 1. Props类型定义 props: { /*...*/ }, // 2. 组合式函数调用 setup(props) { // 3. 响应式状态 // 4. 计算属性 // 5. 方法函数 // 6. 生命周期 // 7. 返回渲染函数 return () => (/*...*/) } })
  3. 注释规范

    /** * 带类型注释的props * @param size - 按钮尺寸 ('small' | 'medium' | 'large') */ interface ButtonProps { size?: 'small' | 'medium' | 'large' }

代码审查要点

TSX特有的审查关注点:

  1. 不必要的响应式解包

    // 错误 <div>{count.value}</div> // 正确 (在setup顶层解包) const unwrappedCount = count.value return () => <div>{unwrappedCount}</div>
  2. 内存泄漏风险

    setup() { const timer = setInterval(/*...*/) onUnmounted(() => clearInterval(timer)) }
  3. 类型安全漏洞

    // 避免any类型 const data: any = fetchData() // 使用泛型 const data = ref<DataType>()

未来演进与社区趋势

Vue与TSX的融合方向

  1. Volar插件的增强支持

    • 更精准的TSX类型推断
    • 模板到TSX的自动转换工具
  2. 编译时优化

    • 更高效的JSX转换策略
    • 更好的Tree-shaking支持
  3. 生态工具链完善

    • 专用的TSX ESLint规则集
    • 性能分析工具集成

备选方案评估

  1. SFC与TSX混合模式

    • 优点:渐进式迁移路径
    • 缺点:上下文切换成本
  2. 纯TSX架构

    • 优点:类型安全最大化
    • 缺点:失去SFC的样式隔离
  3. DSL编译方案

    • 类似Solid.js的编译策略
    • 潜在的性能优势

决策框架与迁移评估

何时选择TSX

适合采用TSX的场景特征:

  1. 复杂逻辑组件

    • 需要大量条件分支
    • 动态渲染逻辑复杂
  2. 类型敏感项目

    • 严格的类型安全要求
    • 大型团队协作
  3. 跨框架组件

    • 需要与React生态共享组件
    • 多框架微前端架构

迁移成本评估模型

考虑因素权重分配:

因素权重评估标准
团队经验30%React/TSX熟悉程度
代码库规模25%现有模板组件数量
类型安全需求20%项目对静态类型的依赖程度
性能要求15%关键路径性能敏感度
工具链成熟度10%现有构建配置支持度

迁移决策公式

总分 = (团队经验×0.3) + (代码库规模×0.25) + (类型安全需求×0.2) + (性能要求×0.15) + (工具链成熟度×0.1) 总分 > 70 → 推荐迁移 总分 40-70 → 部分迁移 总分 < 40 → 暂不迁移

疑难解答与常见陷阱

TSX特有问题排查

  1. 类型扩展问题

    // 扩展原生元素属性 declare module 'vue' { interface HTMLAttributes { customAttr?: string } }
  2. 上下文丢失问题

    // 错误 - this上下文丢失 const handleClick = function() { this.$emit('click') } // 正确 - 使用setup上下文 setup(_, { emit }) { const handleClick = () => emit('click') }
  3. 响应式更新问题

    // 错误 - 直接修改数组 items.value.push(newItem) // 正确 - 创建新引用 items.value = [...items.value, newItem]

性能问题诊断

TSX项目特有的性能陷阱:

  1. 不必要的重新渲染

    // 子组件会随count变化重新渲染 return () => ( <ChildComponent> <span>{count.value}</span> </ChildComponent> )
  2. 大型内联函数

    // 避免在渲染函数内定义大型函数 return () => ( <button onClick={() => { // 复杂逻辑... }} /> )
  3. 深层响应式解包

    // 深层解包影响性能 return () => ( <div>{deepObj.value.a.b.c}</div> )

工具链与开发体验

IDE优化配置

VS Code推荐配置:

{ "typescript.tsdk": "node_modules/typescript/lib", "volar.takeOverMode.enabled": true, "volar.jsx.preserveValueImports": true, "editor.codeActionsOnSave": { "source.organizeImports": true, "source.fixAll.eslint": true } }

调试技巧

TSX组件调试方法:

  1. 渲染快照调试

    console.log('渲染树:', JSON.stringify(renderToStaticMarkup(<Component />), null, 2) )
  2. Props注入调试

    const DebugWrapper = (props: any) => { console.log('当前props:', props) return <Component {...props} /> }
  3. 性能分析标记

    return () => { performance.mark('render-start') const content = <div>{/*...*/}</div> performance.mark('render-end') performance.measure('render', 'render-start', 'render-end') return content }

架构模式进阶

领域驱动设计实现

TSX中的DDD实践:

// modules/user/UserAvatar.tsx const UserAvatar = defineComponent({ setup() { const { user } = useUserContext() return () => ( <div class="user-avatar"> <img src={user.avatarUrl} /> <UserBadges userId={user.id} /> </div> ) } }) // modules/user/context.ts const UserContext = createContext<User>() export const useUserContext = () => useContext(UserContext)

微前端集成方案

TSX组件的微前端适配:

// 作为Web Component导出 defineCustomElement({ props: { userId: String }, setup(props) { const user = fetchUser(props.userId) return () => ( <div class="user-card"> <h3>{user.name}</h3> </div> ) } })

服务端渲染优化

TSX的SSR特殊处理:

// 服务端兼容组件 const ServerCompatComponent = defineComponent({ setup(props, { slots }) { const isServer = typeof window === 'undefined' return () => ( <div> {isServer ? ( <ServerFallback /> ) : ( <ClientComponent /> )} </div> ) } })

样式方案深度探讨

CSS变量与TSX集成

类型安全的CSS变量管理:

// theme.ts export const cssVars = { '--primary-color': '#1890ff', '--border-radius': '4px' } as const type CssVars = typeof cssVars // 在组件中使用 const Button = () => { const style = { [cssVars['--primary-color']]: 'red', // 类型错误,值不可修改 borderRadius: cssVars['--border-radius'] } return <button style={style}>按钮</button> }

原子化CSS实践

TSX中的原子化方案:

// 生成类型安全的class名 const classes = { button: 'py-2 px-4 rounded', primary: 'bg-blue-500 text-white' } as const const Button = () => ( <button class={`${classes.button} ${classes.primary}`}> 原子化按钮 </button> )

样式作用域方案

TSX中的样式隔离策略:

  1. CSS Modules

    import styles from './Button.module.css' <button class={styles.primary} />
  2. Scoped CSS

    // Button.scoped.css .btn { /*...*/ } // Button.tsx <button class="btn" />
  3. Styled Components

    const StyledButton = styled('button', { /*...*/ })

状态管理进阶模式

类型安全的状态机

基于TSX的有限状态机实现:

interface StateMachine<T> { state: T transitions: Record< string, { from: T; to: T; action: () => void } > } const useMachine = <T>(machine: StateMachine<T>) => { const current = ref(machine.state) const transition = (name: string) => { const trans = machine.transitions[name] if (trans.from === current.value) { trans.action() current.value = trans.to } } return { current, transition } } // 使用示例 const { current, transition } = useMachine({ state: 'idle', transitions: { fetch: { from: 'idle', to: 'loading', action: fetchData } } })

响应式存储模式

TSX优化的存储方案:

class UserStore { private state = reactive({ users: [] as User[], loading: false }) readonly users = readonly(state.users) readonly loading = readonly(state.loading) async fetchUsers() { state.loading = true state.users = await api.getUsers() state.loading = false } } // 在组件中使用 const store = new UserStore() return () => ( <div> {store.users.map(user => ( <UserItem key={user.id} user={user} /> ))} </div> )

动画与交互增强

手势交互集成

TSX中的手势识别:

const Button = () => { const bind = useGesture({ onDrag: ({ movement: [mx] }) => { // 处理拖拽逻辑 } }) return () => ( <button {...bind()}> 可拖拽按钮 </button> ) }

动画组件模式

类型安全的动画组件:

interface AnimationProps { duration?: number easing?: string children: (style: CSSProperties) => JSX.Element } const Animate = defineComponent<AnimationProps>({ setup(props) { const progress = ref(0) useAnimationFrame(() => { progress.value = Math.min(progress.value + 0.01, 1) }) return () => props.children({ opacity: progress.value, transform: `scale(${progress.value})`, transition: `all ${props.duration}ms ${props.easing}` }) } }) // 使用示例 <Animate duration={300}> {(style) => <div style={style}>动画内容</div>} </Animate>

无障碍访问支持

ARIA属性集成

TSX中的无障碍增强:

const Alert = defineComponent({ setup(_, { slots }) { const isOpen = ref(false) return () => ( <div role="alert" aria-live="assertive" aria-hidden={!isOpen.value} > {isOpen.value && slots.default?.()} </div> ) } })

焦点管理策略

TSX中的焦点控制:

const Modal = defineComponent({ setup() { const dialogRef = ref<HTMLDialogElement>() onMounted(() => { // 自动聚焦第一个可聚焦元素 dialogRef.value?.querySelector('button')?.focus() }) return () => ( <dialog ref={dialogRef} role="dialog"> {/* 模态内容 */} </dialog> ) } })

国际化方案

类型安全的i18n

TSX中的国际化实现:

// locales/en.ts export default { button: { submit: 'Submit', cancel: 'Cancel' } } as const // 在组件中使用 const Button = defineComponent({ setup() { const { t } = useI18n<typeof import('./locales/en')>() return () => ( <button> {t('button.submit')} </button> ) } })

安全实践

XSS防护策略

TSX中的安全渲染:

const UserComment = defineComponent({ props: { content: String }, setup(props) { const sanitized = computed(() => DOMPurify.sanitize(props.content || '') ) return () => ( <div dangerouslySetInnerHTML={{ __html: sanitized.value }} /> ) } })

移动端适配

触摸事件优化

TSX中的触摸交互:

const Swipeable = defineComponent({ setup(_, { slots }) { const startX = ref(0) const offsetX = ref(0) const onTouchStart = (e: TouchEvent) => { startX.value = e.touches[0].clientX } const onTouchMove = (e: TouchEvent) => { offsetX.value = e.touches[0].clientX - startX.value } return () => ( <div onTouchstart={onTouchStart} onTouchmove={onTouchMove} style={{ transform: `translateX(${offsetX.value}px)` }} > {slots.default?.()} </div> ) } })

测试策略扩展

视觉回归测试

TSX组件的视觉测试方案:

// Button.test.tsx describe('Button视觉测试', () => { it('应该保持一致的渲染结果', () => { const { container } = render(<Button />) expect(container).toMatchImageSnapshot() }) })

交互测试覆盖

复杂交互场景测试:

describe('表单交互测试', () => { it('应该完成多步骤表单提交', async () => { const { getByLabelText, getByText } = render(<MultiStepForm />) await userEvent.type(getByLabelText('用户名'), 'testuser') await userEvent.click(getByText('下一步')) await userEvent.type(getByLabelText('密码'), 'password123') await userEvent.click(getByText('提交')) expect(getByText('提交成功')).toBeInTheDocument() }) })

构建输出优化

代码分割策略

基于路由的分割方案:

// router.ts const routes = [ { path: '/', component: defineAsyncComponent(() => import('./Home.tsx')) }, { path: '/about', component: defineAsyncComponent(() => import('./About.tsx')) } ]

预渲染配置

静态站点生成支持:

// vite.config.ts import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ plugins: [ createHtmlPlugin({ minify: true, entry: '/src/main.tsx' }) ] })

部署策略

CDN优化方案

静态资源部署配置:

// vite.config.ts export default defineConfig({ build: { assetsInlineLimit: 4096, rollupOptions: { output: { assetFileNames: 'assets/[name]-[hash].[ext]' } } } })

性能监控集成

前端监控接入:

// main.tsx const app = createApp(App) app.config.performance = true app.config.errorHandler = (err) => { trackError(err) } // 组件内部监控 const Component = defineComponent({ setup() { onMounted(() => { performance.mark('component-mounted') }) } })

维护与演进

版本升级策略

TSX项目的平滑升级:

  1. 依赖隔离

    { "resolutions": { "@vitejs/plugin-vue-jsx": "1.3.0" } }
  2. 渐进式迁移

    // 旧组件兼容层 export const compatComponent = (comp: any) => {

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询