Vue3使用TSX
最近尝试在Vue3中使用TSX开发一些小玩具。不出所料,还是遇到许多问题的,尝试写一篇博客来记录一下。
TSX支持
Vue3项目中集成tsx还是很简单的,只需要安装官方维护的vite插件pnpm install @vitejs/plugin-vue-jsx -D
,然后在vite配置文件中use一下即可。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [
vue(),
vueJsx()
]
})
然后把目录中以.vue
结尾的文件全部换成.tsx
,接着以如下格式书写即可
import {defineComponent} from 'vue'
export default defineComponent({
setup(props, ctx) {
return () => (
<>
<div>Hello World</div>
</>
);
}
})
其实这个插件的核心还是@vue/babel-plugin-jsx
,只是封装了一层供vite插件调用,因此tsx的一些基础语法可以直接参看@vue/babel-plugin-jsx来学习,本文就不再叙述了。
Props与TSX
既然我们使用的是tsx,那么vue3.2中的script setup
语法糖就无法使用了,因此我们使用vue3.0的方式定义props即可。
import {defineComponent, PropType, Ref, ref} from 'vue'
export default defineComponent({
setup(props, ctx) {
const isShow = ref<boolean>(true)
return () => (
<>
<Children isShow={isShow}/>
<button onClick={()=>isShow.value = !isShow.value}>显示/隐藏</button>
</>
);
}
})
const Children = defineComponent({
props:{
isShow:{
type:Object as PropType<Ref<boolean>>,
required: true,
}
},
setup(props, ctx) {
const {isShow} = props
const inputValue = ref<string>('')
return () => (
<>
<div v-show={isShow.value}>
<input type="text" v-model={inputValue.value}/>
<p>{inputValue.value}</p>
</div>
</>
);
}
})
阅读以上代码可以发现在tsx中用ref
定义的变量,并不会如用template
一样自动解构,还需要手动调用.value。另外v-if、v-for
在tsx中是用不了的,但v-show v-model
还是可以使用的。
Emit与TSX
我们平日里使用的Vue template
中会用 @
去监听一个事件,但在tsx中我们会用on
前缀来替代。
import {defineComponent, PropType, Ref, ref} from 'vue'
export default defineComponent({
setup(props, ctx) {
const handleHello = (str:string)=>{
console.log(str)
}
return () => (
<>
<Children onHello={handleHello}/>
</>
);
}
})
const Children = defineComponent({
emits:['hello'],
setup(props, {emit}) {
return () => (
<>
<button onClick={()=>emit('hello','你好世界')}>emit</button>
</>
);
}
})
这里有个小坑,就是子组件使用emit时,一定要先使用emits:['hello']
来声明这个自定义事件,否则父组件会报ts错误。当然你也可以不声明,把父组件改成<Children {...{onHello:handleHello}}/>
就可以了。
Slot与TSX
默认插槽
Vue 的 slots 在tsx中也有些改变,我们先来看看默认插槽
import {defineComponent, PropType, Ref, ref} from 'vue'
export default defineComponent({
setup(props, ctx) {
return () => (
<>
<Children>
<p>这是默认插槽</p>
</Children>
</>
);
}
})
const Children = defineComponent({
setup(props, {slots}) {
return () => (
<>
{slots.default?.()}
</>
);
}
})
发现我们得调用slots.default?.()
来获取插槽内容
具名插槽
import {defineComponent, PropType, Ref, ref} from 'vue'
export default defineComponent({
setup(props, ctx) {
return () => (
<>
<Children v-slots={{
header: () => (
<>这是header插槽</>
)
}}>
</Children>
</>
);
}
})
const Children = defineComponent({
setup(props, {slots}) {
return () => (
<>
{slots.header?.()}
</>
);
}
})
可以看到,父组件通过 v-slots 属性去定义插槽。当然默认插槽也可以放在v-slots里面定义。default: () => (<>这是默认插槽</>),
作用域插槽
import {defineComponent} from 'vue'
export default defineComponent({
setup(props, ctx) {
return () => (
<>
<Children v-slots={{
content:(scope:{name:string})=> <>{scope.name}</>
}}>
</Children>
</>
);
}
})
const Children = defineComponent({
setup(props, {slots}) {
return () => (
<>
{slots.content?.({name:'suemor'})}
</>
);
}
})
如示例所示,传参即可。
结尾
总而言之Vue3搭配tsx坑还挺多的,比如与一些组件库搭配起来可能会出现各种奇怪的问题,这个以后再说吧,今天就先到这里了。