This article explains how to use TSX in Vue 3
Let me start with two questions:
Why Vue 3
In Vue 2, component-related logic is written in a single file, often with thousands of lines of code per component, which is very difficult to maintain. In addition, the relevant codes of a function are usually scattered in various options such as data methods and created, so it is difficult to reuse a function. The composite API of Vue 3 is designed to solve this problem.
Why tsx
The components in Vue are a black box, and you don’t know their internal structure (properties and events). You have to document it or even read the source code, which can be time consuming. In TSX, when writing code, you can get the code prompt, the internal structure of the component can be clear at a glance, and code binding (when you write a wrong code will get an error), which can greatly improve the efficiency of code writing, reduce low-level errors.
The type declaration of the component
Props
Ts will infer the types of properties from the PROPS declaration of the VUE component. Here are some basic type inferences:
- String, the String
- Number and the Number
- Boolean, Boolean
- The Array – unknown []
- Object – > Record < string, any >
- The Date to string
- The Function and the Function
- Symbol and Symbol
You can use PropType for more precise type declarations. It is recommended that all Array Object functions be declared
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>}})Copy the code
Attribute is optional, by default the title in the above example the default will be a string | undefined type. If you declare required: true, its type will become string.
Some third-party components may lack type declarations, which can lead to typescript type checking errors. *** Properties do not exist, in which case it is recommended to use attribute deconstruction instead of typescript type checking
// Property 'onClick' does not exist on type ...
<ElButton onClick={onClickBtn}></Elbutton>
// Destruct with attributes
<ElButton {.{onClick: onClickBtn} as any} ></Elbutton>
Copy the code
Events
Events are syntax candy for function props, @click=”myCallback” is equivalent to onClick={myCallback}. It is strongly recommended that you avoid using events and use their equivalent method properties instead, as you will get better type hints.
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={... } / >Copy the code
Is equivalent to
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={... } / >Copy the code
TSX grammar
You can continue to use the template template, but we recommend you use the TSX template.
<template>
<div>contents</div>
</template>
<script lang="tsx">
defineComponent({
setup(props) {
return {
// ...
}
}
})
</script>
Copy the code
or
<script lang="tsx">
defineComponent({
setup(props) {
return () => (
<div>contents</div>
)
}
})
</script>
Copy the code
Attributes / Props
// template
<input type="email" :placeholder="placeholderText" />
// tsx
<input type="email" placeholder={placeholderText} />
Copy the code
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} />
Copy the code
v-models
// template
<A v-model="foo" v-model:bar="bar" />
// tsx
<A v-models={[[foo], [bar,"bar} / "]] >
Copy the code
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} />; }};Copy the code
Hooks
Hooks are a concept borrowed from React. The Hooks in Vue 3 are called composite apis that include the responsive apis provided by Vue 3 (REF, Reactive, computed, onMounted…). , and their own hook functions.
Hooks logically break up messy code, make it easy to reuse similar functions, and handle large, complex components with ease.
Here is an example from the Vue official website where the functionality of the component is split into different 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
}
}
Copy the code
In React, there are some rules to follow when using Hooks. In Vue 3, the Hooks rule is used slightly differently:
1. Use hooks only at the top level. Do not call hooks in loops, conditions, or nested functions
Since Vue 3 uses Proxy to track data changes, the order of Hooks calls does not affect data dependencies, so this rule does not have to be followed.
2. Only inReactCall hooks in the (Setup and Hook) functions, not in normal JavaScript functions
This rule still applies to Vue 3. Hooks can be used to save state, whereas normal functions cannot. When the data dependency changes, the function is re-executed, and if there happens to be some state declared in the function, such as the ref object, that state can be reset, resulting in unexpected situations. So hooks can only be called from Setup functions and other hooks.
// ❎ Do not call hook(ref) in normal functions.
function renderInput() {
// inputValue is reset when input is re-rendered
const inputValue = ref(' ')
return <input v-model={inputValue.value} />
}
Copy the code
// ✅ call hook(ref) in setup
setup() {
const inputValue = ref(' ')
return (
{renderInput(inputValue)}
)
}
// Pass inputValue as an argument
function renderInput(inputValue) {
return <input v-model={inputValue.value} />
}
Copy the code
Problems
Currently, Vue 3 is not 100% ts support, so there are still some issues, such as:
- TSX code cannot be hot updated, must be manually refreshed (as of 2021/4/30)
<script lang="tsx">
defineComponent({
setup(props) {
return () => (
<div>the text here won't change with HMR</div>
)
}
})
</script>
Copy the code
- Default values for functional properties interfere with typescript type inference, need to be changed to arrow functions (as of 2021/4/30)
// ❎ interferes with typescript type inference
export default defineComponent({
props: {
objectProp: {
type: Object.default() {
return{}}},arrayProp: {
type: Array.default() {
return[]}}}})// ✅ use the arrow function
export default defineComponent({
props: {
objectProp: {
type: Object.default: () = >({})},arrayProp: {
type: Array.default: () = >[]}}})Copy the code
References
- 3 typescript Vue support (https://v3.cn.vuejs.org/guide/typescript-support.html)
- jsx-next repo (https://github.com/vuejs/jsx-next)
- Vue 3 modular API (https://v3.cn.vuejs.org/guide/composition-api-introduction.html)