本文将介绍如何在Vue 3中应用tsx
首先答复两个问题:
Why Vue 3
在Vue 2中,组件相干的逻辑都被写在一个文件中,常常会呈现一个组件几千行代码,十分难以保护;而且一个性能的相干代码通常会扩散写在data
methods
created
等各个选项中,想要复用一个性能也是比拟艰难的。而Vue 3的组合式API正是为了解决这个问题。
Why tsx
Vue中的组件是一个黑盒,你不晓得他的内部结构(属性和事件),只能通过文档甚至浏览源码来理解,这样是很消耗工夫的。而在tsx中,在编写代码时就能够取得代码提醒,组件的内部结构能够高深莫测,且具备代码约束力(当你编写了谬误的代码时会取得一个报错),这样是能够极大的进步代码编写效率,缩小低级谬误呈现。
组件的类型申明
Props
ts会从vue组件的props
申明中推断属性的类型,以下是一些根本的类型推断:
- String → string
- Number → number
- Boolean → boolean
- Array → unknown[]
- Object → Record<string, any>
- Date → string
- Function → Function
- Symbol → symbol
你能够通过PropType
来进行更加准确的类型申明,倡议对所有的Array
Object
Function
都进行申明
import { defineComponent, PropType } from 'vue'export default defineComponent({ props: { title: String, values: { type: Array as PropType<number[]>, required: true, }, data: Object as PropType<{ id: number, name: string }>, onClick: Function as PropType<(id: number) => void> }})
属性默认是可选的,在下面例子中的title
默认会失去一个string | undefined
的类型。如果你申明了required: true
,他的类型就会变成string
。
有一些第三方组件可能会短少类型申明,这会导致typescript类型查看谬误***属性并不存在
,这种状况倡议应用属性解构,避开typescript类型查看
// Property 'onClick' does not exist on type ...<ElButton onClick={onClickBtn}></Elbutton>// 应用属性解构<ElButton {...{onClick: onClickBtn}}></Elbutton>
Events
Events实际上是function props
的语法糖,@click="myCallback"
等价于onClick={myCallback}
。强烈建议防止应用Event
,而应用与之等效的办法属性,这样你会取得更好的类型提醒。
defineComponent({ emits: { // event runtime validation 'play': (value: string) => { return true }, 'rest-time': () => true }, setup(props, {emit}) { onMounted(() => { emit('play', 'game') emit('rest-time') }) }})<MyComponent onPlay={...} onRest-time={...} />
等价于
defineComponent({ props: { onPlay: Function as PropType<(value: string) => void>, 'onRest-time': Function as PropType<() => void>, }, setup(props) { onMounted(() => { props.onPlay('game') props['onRest-time']() }) }})<MyComponent onPlay={...} onRest-time={...} />
TSX语法
你能够持续应用template模板,不过咱们更倡议你应用tsx模板。
<template> <div>contents</div></template><script lang="tsx">defineComponent({ setup(props) { return { // ... } }})</script>
或者
<script lang="tsx">defineComponent({ setup(props) { return () => ( <div>contents</div> ) }})</script>
Attributes / Props
// template<input type="email" :placeholder="placeholderText" />// tsx<input type="email" placeholder={placeholderText} />
Directives
v-model
// template<input v-model="val" /><input v-model:name="val"><input v-model.trim="val"><input v-model:name.trim="val">// tsx<input v-model={val} /><input v-model={[val, 'name']} /><input v-model={[val, ['trim']]} /><input v-model={[val, 'name', ['trim']]} />// bind ref value<input v-model={valueRef.value} />
v-models
// template<A v-model="foo" v-model:bar="bar" />// tsx<A v-models={[[foo], [bar, "bar"]]} />
slot
const A = (props, { slots }) => ( <> <h1>{ slots.default ? slots.default() : 'foo' }</h1> <h2>{ slots.bar && slots.bar() }</h2> </>);const App = { setup() { const slots = { default: () => <div>A</div>, bar: () => <span>B</span>, }; return () => <A v-slots={slots} />; },};
Hooks
Hooks是从React借鉴过去的概念,Vue 3中的Hooks叫做组合式API,它包含Vue 3提供的响应性api(ref
, reactive
, computed
, onMounted
...),以及本人编写的hook函数。
Hooks能够对凌乱的代码做逻辑拆分,类似的性能能够不便的进行复用,通过Hooks的灵便组合,能够轻松解决大型简单组件。
以下是Vue官网的例子,组件的性能被拆分到不同的Hooks中。
setup(props) { const { user } = toRefs(props) const { repositories, getUserRepositories } = useUserRepositories(user) const { searchQuery, repositoriesMatchingSearchQuery } = useRepositoryNameSearch(repositories) const { filters, updateFilters, filteredRepositories } = useRepositoryFilters(repositoriesMatchingSearchQuery) return { repositories: filteredRepositories, getUserRepositories, searchQuery, filters, updateFilters }}
在React中,应用Hooks时须要恪守一些规定。不过Vue 3的渲染机制不同于React,在Vue 3中应用Hooks规定稍有不同:
1. 只在最顶层应用 Hook, 不要在循环,条件或嵌套函数中调用 Hook
因为Vue 3应用Proxy来追踪数据扭转,Hooks调用的程序不会影响数据的依赖关系,所以这条规定不用恪守。
2. 只在 React(Setup 和 Hook) 函数中调用 Hook,不要在一般的 JavaScript 函数中调用 Hook
这条规定依然实用于Vue 3。Hooks能够用来保留状态,而一般函数不能够。当数据依赖扭转时,函数会被从新执行,如果刚好函数中申明了一些状态,例如ref
对象,那么这个状态就会被重置,从而导致意外的状况产生。所以Hook只能在Setup函数以及其余Hook中调用。
// ❎ 不要在一般函数中调用hook(ref)function renderInput() { // 当input从新渲染时,inputValue会被重置 const inputValue = ref('') return <input v-model={inputValue.value} />}
// ✅ 在setup中调用hook(ref)setup() { const inputValue = ref('') return ( {renderInput(inputValue)} )}// 通过参数传递inputValuefunction renderInput(inputValue) { return <input v-model={inputValue.value} />}
Problems
目前,Vue 3对ts的反对度还没有达到100%,所以仍存在一些问题,例如:
tsx代码无奈热更新,需手动刷新(截至2021/4/30)
<script lang="tsx">defineComponent({ setup(props) { return () => ( <div>the text here won't change with HMR</div> ) }})</script>
函数模式的属性默认值会烦扰typescript类型推断,需改为箭头函数(截至2021/4/30)
// ❎ 会烦扰typescript类型推断export default defineComponent({ props: { objectProp: { type: Object, default() { return {} } }, arrayProp: { type: Array, default() { return [] } } }})// ✅ 应用箭头函数export default defineComponent({ props: { objectProp: { type: Object, default: () => ({}) }, arrayProp: { type: Array, default: () => [] } }})
References
- Vue 3 typescript反对 (https://v3.cn.vuejs.org/guide/typescript-support.html)
- jsx-next repo (https://github.com/vuejs/jsx-next)
- Vue 3 组合式api (https://v3.cn.vuejs.org/guide/composition-api-introduction.html)