如上报错,问题在于之前安装了 React 相关的东西,可能导致 TSX 解析环境混乱,因为 React 和 Vue 的 TSX 解析方式不同。
可以按照以下步骤彻底排查和修复:
1. 检查是否安装了 React 相关依赖
如果之前安装了 React 相关的依赖,比如 react,react-dom,@types/react,Vue 的 TSX 解析可能会受到影响。
npm list --depth=0 | grep react
如果存在相关依赖,彻底删除:
npm uninstall react react-dom @types/react
然后 清理依赖 并重新安装:
rm -rf node_modules package-lock.json pnpm-lock.yaml yarn.lock
npm cache clean --force
npm install
2. 确保 TypeScript 版本正确
Vue 3 的 JSX 解析需要 TypeScript >= 5.x,如果版本过低,可能会导致 "XX 不能用作 TSX 组件" 的错误。
检查 TypeScript 版本
npx tsc -v
如果版本低于 5.x 建议升级:
npm install typescript@latest -D
3. 确保正确安装 Vue 相关的 JSX 支持
Vue 默认不支持 JSX,需要安装 @vue/babel-plugin-jsx 才能正确解析 JSX 语法。
安装 Vue 相关的 JSX 依赖:
npm install @vue/babel-plugin-jsx -D
然后,在 babel.config.js 里启用:
module.exports = {
plugins: ["@vue/babel-plugin-jsx"],
};
如果使用的是 Vite,还需要安装:
npm install @vitejs/plugin-vue-jsx -D
然后在 vite.config.ts 里添加:
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [vueJsx()],
});
按照这个顺序排查,应该就能解决问题了!
4. 再补充一点(关键)
如果上述解决不了,那就在 tsconfig.json 文件下添加:
"jsxFactory": "h",
为什么呢?这其实是 Vue JSX 和 TypeScript 的编译机制不一致导致的。
因为默认情况下,TypeScript 会将 JSX 编译成 React 风格的代码(即调用 React.createElement()),而不是 Vue 需要的 h() 函数!
所以当没有添加 jsxFactory: "h",TypeScript 就不知道我们要用 Vue,而是默认以为在用 React。这就导致:
- Vue 组件没有 React 的 props 类型结构;
- TypeScript 无法理解写的 JSX 是 Vue 的组件;
所以就报出类似:JSX 元素类不支持特性,因为它不具有“props”属性。
具体细节
🎯 没设置 jsxFactory 时,TS 编译 JSX 的行为:
默认情况下,TypeScript 会把如下 JSX:
<MyComponent title="Hello" />
编译成:
React.createElement(MyComponent, { title: "Hello" });
但在 Vue 中,应该是:
h(MyComponent, { title: "Hello" });
jsxFactory: "h" 的作用是告诉 TypeScript:“别用 React.createElement 了!我要用 Vue 的 h() 来编译 JSX!”
这样 TypeScript 就能正确识别 h() 是 Vue 的创建函数,对 Vue 组件进行类型推导,就不会报 props 错了。
关键
如果使用 Vue 3 + JSX,建议确保以下几项配置都正确:
1、tsconfig.json
{
"compilerOptions": {
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
2、vite.config.ts
确保启用了 Vue 的 JSX 插件:
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [vue(), vueJsx()],
})
补充说明:为啥 Vue 官方默认不加这个?
因为 Vue 的 SFC(单文件组件)并不使用 JSX,只有在写 .tsx 或 .jsx 的时候才需要加这些配置。
5. 小 Tip
jsxFactory: "h"
jsxFactory 指定了 JSX 元素应该使用哪个函数作为工厂来创建元素。默认情况下,React 使用 React.createElement 作为工厂函数,而在 Vue 3 中,我们通常使用 h 作为 JSX 元素的工厂函数。
为什么使用 h?
在 Vue 3 中,h 是 createVNode 的一个简写,h 函数负责创建虚拟 DOM 节点。它是 Vue 中处理 JSX 的标准工厂函数。当在 Vue 3 项目中使用 JSX 时,h 就是构建组件或 HTML 元素的函数。
所以,"jsxFactory":"h" 的作用就是告诉 TypeScript 在编译 JSX 时,应该用 h 函数来处理 JSX 元素的创建。
举个 🌰:
import { h } from 'vue'
const MyComponent = () => {
return <div>Hello, world!</div> // 这里会被转换成 h('div', null, 'Hello, world!')
}
jsxFragmentFactory: "Fragment"
jsxFragmentFactory 指定了在 JSX 中使用 <> 和 </>(即 Fragment)时,应该使用哪个函数来生成对应的虚拟节点。
为什么使用 Fragment?
在 Vue 3 中,Fragment 是一个特殊的 Vue 组件,它不渲染任何元素,只是一个包裹多个子节点的容器。这使得可以在 JSX 中返回多个根节点,而无需使用额外的元素包裹它们。
Fragment 是 Vue 3 内置的功能,用于支持 JSX 语法中的片段(Fragment),即没有根节点的多个子节点。
"jsxFragmentFactory":"Fragment" 的作用就是告诉 TypeScript 在编译 JSX 时,应该用 Fragment 来处理 JSX 中的片段。
举个 🌰:
import { Fragment } from 'vue'
const MyComponent = () => {
return <>
<div>First element</div>
<div>Second element</div>
</> // 这里会被转换成 Fragment([], [h('div', null, 'First element'), h('div', null, 'Second element')])
}
在这个例子中,<> 和 </> 包裹了两个 div 元素,Fragment 负责将它们组合在一起,而不会在 DOM 中创建额外的父节点。
这两个选项是配合 Vue 3 和 TypeScript 使用 JSX 时非常重要的配置项,确保代码能正确编译和运行。