With the release of Vue3.2, the < Script Setup > syntax sugar was released, and writing Vue components with typescript is a much more comfortable experience. This article summarizes some common writing methods in

Development environment setup

  • It is recommended to useviteTo quickly initialize a Vue3+Typescript project, which configures some configuration out of the box
  • It is recommended to use vscode and install the volar plug-in to assist development
  • It is recommended to customize a code snippet to aid development
    // vue.json
    {
      "vue setup ts less": {
      	"prefix": "vstl"."body": [
      		"<template>"." 
            
    "
    ."</template>"."<script setup lang=\"ts\">".""."</script>"."<style lang=\"less\" scoped>"."</style>",]."description": "vue3 setup ts less"}}Copy the code

Use reactive variables

Reactive variables defined in

  • useref:refThe initial value passed in is typed, and if it is a complex type, it can be specified by a generic parameter
<template>
    <div>{{ numberRef }}</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
const stringRef = ref("") // ===> Ref<string>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
</script>
Copy the code
  • usereactiveThe Vue documentation describes three ways to do thisreactiveDeclare the type
<template>
    <div>{{ book1.bar }}</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
interface IFoo {
    bar: string
}
/ / the first
const book1 = reactive<IFoo>({ bar: 'bar' })
/ / the second
const book2: IFoo = reactive({ bar: 'bar' })
/ / the third kind
const book3 = reactive({ bar: 'bar' }) as IFoo
</script>
Copy the code

usecomputed

A computed method automatically derives the type

  • Basic mode of use
<template>
    <div>{{ fooComputed? .bar }}</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
const numberComputed = computed(() = > numberRef.value) // ===> ComputedRef<number>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
const fooComputed = computed(() = > {
    return fooRef.value
}) // ===> ComputedRef<IFoo | undefined>
</script>
Copy the code
  • Can be writtencomputed
<template>
    <div>{{ fooComputedWritable? .bar }}</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
const fooComputedWritable = computed({
    get: () = > {
        return fooRef.value
    },
    set: (value) = > {
        fooRef.value = value
    }
}) // ===> WritableComputedRef<IFoo | undefined>
</script>
Copy the code

usewatchandwatchEffect

You can use the Watch and watchEffect methods directly

<template>
    <div>{{ numberRef }}</div>
</template>
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>

watchEffect(() = > console.log(fooRef.value? .bar)) watch(numberRef,() = > {
    console.log(The 'numberRef' changes)})const stop = watch(fooRef, () = > {
    console.log('fooRef changes')}, {deep: true
}) // Check for deeply nested objects or arrays
stop(); // Stop listening
</script>
Copy the code

usenextTick

Introduce the nextTick method directly

<template>
    <div></div>
</template>
<script setup lang="ts">
import { nextTick } from 'vue';

nextTick(() = > {
    // ...
})

// Async /await can also be used
async() = > {await nextTick()
    / /...
}
</script>
Copy the code

Use other components

Simply import other components and use them in the template. Imported components are automatically registered. Note that when using dynamic components, you should use dynamic: IS bindings

<template>
    <Foo></Foo>
    <foo-item></foo-item>
    <component :is="Foo" />
    <component :is="someCondition ? Foo : FooItem" />
</template>
<script setup lang="ts">
import Foo from "./Foo.vue"
import FooItem from "./FooItem.vue"
const someCondition = false
</script>
Copy the code

The statement props

DefineProps is used to declare props, either without generics or with generics, and both can declare types and default values at the same time

  • Non-generic: it accepts andpropsOptions with the same value.
<template>
    <div>
        <! ToRefs conversion is not required -->
        {{ foo }}
    </div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue';
interface ICustomType {
    foo: string,
    bar: string
}
const props = defineProps({
    foo: String.// Use constructors to declare types
    fooMultiTypes: [String.Number].// Multiple types
    fooCustomType: Object as () => ICustomType, // Custom type
    fooCustomTypeWithRequire: {
        type: Object as () => ICustomType,
        required: true
    }, // User-defined type, mandatory
    fooCustomTypeWithDefault: {
        type: Object as () => ICustomType,
        default: () = > {
            return {
                foo: "foo".bar: "bar"}}},// Custom type with default value
})

// 1. You can use the declared props in the template 
       
// 2. If a value needs to be used in setup, it needs to be converted with toRefs and then deconstructed
const {
    foo,  // ===> Ref<string | undefined> | undefined
    fooMultiTypes, // ===> Ref<string | number | undefined> | undefined
    fooCustomType, // ===> Ref<ICustomType | undefined> | undefined
    fooCustomTypeWithRequire, // ===> Ref<ICustomType>
} = toRefs(props)
</script>
Copy the code
  • Generic mode: declare the default value at this time, requiredwithDefaultsThe compiler macros
<template>
    <div>
        <! ToRefs conversion is not required -->
        {{ foo }}
    </div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue';
interface ICustomType {
    foo: string,
    bar: string
}
constprops = defineProps<{ foo? : string,fooWithRequire: string,
    fooMultiTypes: string | number, fooCustomType? : ICustomType,fooCustomTypeWithRequire: ICustomType
}>()

// Declare default values in a generic manner, using the withDefaults compiler macro
const propsWithDefault = withDefaults(
    defineProps<{
        fooCustomTypeWithDefault: ICustomType
    }>(),
    {
        fooCustomTypeWithDefault: () = > {
            return {
                foo: "foo".bar: "bar"}}})// 1. You can use the declared props in the template 
       
// 2. If a value needs to be used in setup, it needs to be converted with toRefs and then deconstructed
const {
    foo,  // ===> Ref<string | undefined> | undefined
    fooWithRequire,  // ===> Ref<string>
    fooMultiTypes, // ===> Ref<string | number>
    fooCustomType, // ===> Ref<ICustomType | undefined> | undefined
    fooCustomTypeWithRequire, // ===> Ref<ICustomType>
} = toRefs(props)

const {
    fooCustomTypeWithDefault,  // ===> Ref<ICustomType>
} = toRefs(propsWithDefault)
</script>
Copy the code

The statement emits

Declare it using defineEmits, which accepts the same value as the emits option.

<template>
    <div @click="emitsWithObject('bar', $event)"></div>
</template>
<script setup lang="ts">
interface IUserInput {
    email: string,
    password: string
}
// Array mode
const emitsWithArray = defineEmits(["foo"."bar"])
// Object mode
const emitsWithObject = defineEmits({
    // Take a validation function and specify the load type
    foo: (payload: IUserInput) = > {
        if (payload.email && payload.password) {
            return true
        } else {
            console.warn(`Invalid submit event payload! `)
            return false}},// Specify only the load type
    bar: (evevnt: MouseEvent) = > true
})
// Generic
const emitsWithGeneric = defineEmits<{
    (e: 'foo'.id: number): void
    (e: 'bar'.value: string): void} > ()// Trigger the event
emitsWithArray("foo")
emitsWithObject("foo", { email: "[email protected]".password: 'bp' })
emitsWithGeneric("foo".1)
</script>
Copy the code

Recursive components

A single-file component can be referenced by itself by its filename, but in the case of named flushing, use the import alias import to avoid collisions

// FooBar.vue
<template>
    <div>
        <! A single-file component can be referenced by itself by its filename -->
        <FooBar></FooBar>
        <foo-bar></foo-bar>

        <foo-bar-other></foo-bar-other>
    </div>
</template>
<script setup lang="ts">
// Use import alias imports to avoid collisions
import { default as FooBarOther } from './others/FooBar.vue'
</script>
Copy the code

Lifecycle hooks are used

Use the corresponding lifecycle hook function directly

<template>
    <div></div>
</template>
<script setup lang="ts">
import {
    onBeforeMount,
    onMounted,
    onBeforeUpdate,
    onUpdated,
    onBeforeUnmount,
    onUnmounted,
    onErrorCaptured,
    onRenderTracked,
    onRenderTriggered,
    onActivated,
    onDeactivated
} from "vue"

// Just use it
onMounted(() = > {
    // ...
})
</script>
Copy the code

Exposing component properties

Variables in

  • inBar.vueTo expose attributes due to usedefineExposeThe exposed property type is not yet availableInstanceType<typeof Bar>This way to extract, specific can refer to this# 4397, so it needs to be usedexportManually export exposed types.
<template>
  <div></div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const a = 1
const b = ref(2)

const exposeValue = {
  a,
  b
}
defineExpose(exposeValue)
export type IExposeType = typeof exposeValue
</script>
Copy the code
  • inFoo.vueThrough theThe template referenceUse exposed properties
<template>
    <bar ref="barRef"></bar>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Bar, { IExposeType } from './Bar.vue';
const barRef = ref<IExposeType>();
consta = barRef.value? .a// number | undefined
constb = barRef.value? .b// Ref<number> | undefined
</script>
Copy the code

Use provide and inject

Provide and Inject support for generics to control the types of injected variables

  • inFoo.vueThe use ofprovideProvide dependencies that need to be injected
<template>
    <div></div>
</template>
<script setup lang="ts">
import { provide } from 'vue';
export interface IUser {
    name: string,
    age: number
}
provide("name"."foo")
provide<IUser>("user", {
    name: "foo".age: 23
})
</script>
Copy the code
  • inBar.vueThe use ofinjectIntroduce injected dependencies
<template>
    <div></div>
</template>
<script setup lang="ts">
import { inject } from 'vue';
import { IUser } from './Foo.vue';

const name = inject<string>("name") // ===> string | undefined
const user = inject<IUser>("user") // ===> IUser | undefined
</script>
Copy the code

😁 : VueCook, an open source front-end low code tool written by the author, has been released, this is its introduction article, its official documentation is here: VueCook, Github repository is here: VueCook- Github, welcome to comment 😁