1. Introduction

This article focuses on the basics of < Script Setup > and TypeScript.

What is

< Script Setup > is a compile-time syntactic sugar for using composition API in single-file components (SFC).

At the time of this writing, vUE is using version 3.2.26.

1.1. Development history

Let’s look at the evolution of vue3

  • Vue3In earlier versions (3.0.0 - beta. 21Before)composition apiSupport is available only in component optionssetupFunction.
<template> <h1>{{ msg }}</h1> <button type="button" @click="add">count is: {{ count }}</button> <ComponentA /> <ComponentB /> </template> <script> import { defineComponent, ref } from 'vue' import ComponentA from '@/components/ComponentA' import ComponentB from '@/components/ComponentB' export default defineComponent({ name: 'HelloWorld', components: { ComponentA, ComponentB }, props: { msg: String, }, setup(props, CTX) {const count = ref(0) function add() {count.value++} return {count, add,}}, }) </script>Copy the code
<script setup lang="ts">
import { ref } from 'vue'
import ComponentA from '@/components/ComponentA'
import ComponentB from '@/components/ComponentB'

defineProps<{ msg: string }>()

const count = ref(0)

function add() {
  count.value++
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button type="button" @click="add">count is: {{ count }}</button>
  <ComponentA />
  <ComponentB />
</template>
Copy the code

Advantage of 1.2.

Advantages of

  • Less, cleaner code, no need to usereturn {}Exposed variables and methods, no active registration is required when using components;
  • betterTypescriptSupport, use pureTypescriptThe statementpropsAnd throw events are no longer like thatoption apiIs so lame;
  • Better runtime performance;

Of course, < Script Setup > has its drawbacks, such as the need to learn additional apis.

How to use

2. Use bullet points

2.1. Tools

Vue3 single file component (SFC) TS IDE support please use

Run the vue-tsc command to check the type.

  • VSCode: The front end is bestIDE.
  • Volar: in order toVue3*.vueSingle-file components provide support for code highlighting, syntax hints, and moreVSCodePlug-in;Vue2You may be usingVeturPlugins need to be disabledVeturDownload,VolarAnd enable it.
  • vue-tsc: Type check anddtsBuild command line tools.

2.2. Basic Usage

Add the setup property to the

<script setup>
import { ref } from 'vue'

defineProps({
  msg: String
})

const count = ref(0)

function add() {
  count.value++
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button type="button" @click="add">count is: {{ count }}</button>
</template>
Copy the code

To use TypeScript, add the lang attribute to the

<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)

function add() {
  count.value++
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button type="button" @click="add">count is: {{ count }}</button>
</template>
Copy the code

The script in the

Top-level bindings declared in < Script Setup > (variables, functions, imported content) are automatically exposed to the template and used directly in the template.

<script setup>
import { ref } from 'vue'
// 外部引入的方法,不需要通过 methods 选项来暴露它,模板可以直接使用
import { getToken } from './utils'
// 外部引入的组件,不需要通过 components 选项来暴露它,模板可以直接使用
import ComponentA from '@/components/ComponentA'

defineProps({
  msg: String
})
// 变量声明,模板可以直接使用
const count = ref(0)
// 函数声明,模板可以直接使用
function add() {
  count.value++
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <h1>{{ getToken() }}</h1>
  <button type="button" @click="add">count is: {{ count }}</button>
  <ComponentA />
</template>
Copy the code

Note:

  • Each *.vue file can contain up to one

  • Each *.vue file can contain up to one

2.3. Compiler macros

** Compiler macros ** include: defineProps, defineEmits, withDefaults, defineExpose, etc.

Compiler macros can only be used in < Script Setup > blocks, do not need to be imported, and are compiled away when processing < Script Setup > blocks.

Compiler macros must be used at the top level of < Script Setup > and may not be referenced in local variables of < Script Setup >.

defineProps

There are no component configuration items in the

<script setup> const props = defineProps({MSG: String, title: {type: String, default: 'I'm the title'}, list: {type: Array, default: () => []}}) // Use the properties console.log(props. MSG) </script> <template> <! - a variable declared in a direct use of props in the template - > < h1 > {{MSG}} < / h1 > < div > {{title}} < / div > < / template >Copy the code

TS version:

<script setup lang="ts"> interface ListItem { name: string age: number } const props = defineProps<{ msg: string title: string list: ListItem[]}>() // use properties in props in ts, Console. log(props. List [0].age) </script> <template> <h1>{{MSG}}</h1> <div>{{title}}</div> </template>Copy the code

In this code, we can see that props does not define a default value.

Vue3 provides us with the withDefaults compiler macro, which provides default values for props.

<script setup lang="ts"> interface ListItem {name: string age: number} interface Props {MSG: string // title : string list: ListItem[]} // the second argument to withDefaults is the default argument setting, Const props = withDefaults(defineProps< props >(), {title: 'I am the title ', // For array and object we need to use the function list as before: () => []}) // Use properties in props in ts, Console. log(props. List [0].age) </script> <template> <h1>{{MSG}}</h1> <div>{{title}}</div> </template>Copy the code

One caveat: declaring a variable at the top level with the same name as the props property is a bit of a problem.

<script setup> const props = defineProps({ title: { type: String, default: Const title = '123' </script> <template> <! <div>{{props. Title}}</div> <! <div>{{title}}</div> </template>Copy the code

So, as with component options, do not define a top-level variable with the same name as the properties of props.

defineEmits

Similarly, there is no component configuration item emits in the

// ./components/HelloWorld.vue
<script setup>
defineProps({
  msg: String,
})

const emits = defineEmits(['changeMsg'])

const handleChangeMsg = () => {
  emits('changeMsg', 'Hello TS')
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button @click="handleChangeMsg">handleChangeMsg</button>
</template>
Copy the code

Using components:

<script setup>
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
    
const msg = ref('Hello Vue3')
const changeMsg = (v) => {
  msg.value = v
}
</script>

<template>
  <HelloWorld :msg="msg" @changeMsg="changeMsg" />
</template>
Copy the code

TS version:

// ./components/HelloWorld.vue
<script setup lang="ts">

defineProps<{
  msg: string
}>()

const emits = defineEmits<{
  (e: 'changeMsg', value: string): void
}>()

const handleChangeMsg = () => {
  emits('changeMsg', 'Hello TS')
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button @click="handleChangeMsg">handleChangeMsg</button>
</template>
Copy the code

Using components:

<script setup lang="ts">
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
const msg = ref('Hello Vue3')
const changeMsg = (v: string) => {
  msg.value = v
}
</script>

<template>
  <HelloWorld :msg="msg" @changeMsg="changeMsg" />
</template>
Copy the code

defineExpose

In Vue3, any bindings declared in < Script Setup > are not exposed by default, that is, bindings declared by component instances cannot be retrieved from the template ref.

Vue3 provides the defineExpose compiler macro, which explicitly exposes variables and methods declared in the component that you want to expose.

// ./components/HelloWorld.vue <script setup> import { ref } from 'vue' const msg = ref('Hello Vue3') const HandleChangeMsg = (v) => {msg.value = v} // Expose({MSG, handleChangeMsg, }) </script> <template> <h1>{{ msg }}</h1> </template>Copy the code

Using components:

<script setup>
import { ref, onMounted } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

const root = ref(null)

onMounted(() => {
  console.log(root.value.msg)
})

const handleChangeMsg = () => {
  root.value.handleChangeMsg('Hello TS')
}
</script>

<template>
  <HelloWorld ref="root" />
  <button @click="handleChangeMsg">handleChangeMsg</button>
</template>
Copy the code

TS version:

// ./components/HelloWorld.vue
<script setup lang="ts">
import { ref } from 'vue'
const msg = ref('Hello Vue3')

const handleChangeMsg = (v: string) => {
  msg.value = v
}

defineExpose({
  msg,
  handleChangeMsg
})
</script>

<template>
  <h1>{{ msg }}</h1>
</template>
Copy the code

Using components:

<script setup lang="ts"> import { ref, OnMounted} from 'vue' import HelloWorld from '. / components/HelloWorld. Vue '/ / temporarily use any here, Const root = ref<any>(null) onMounted(() => {console.log(root.value.msg)}) const handleChangeMsg = () => { root.value.handleChangeMsg('Hello TS') } </script> <template> <HelloWorld ref="root" /> <button @click="handleChangeMsg">handleChangeMsg</button> </template>Copy the code

2.4. Auxiliary functions

UseAttrs, useSlots, useCssModule. The other functions are still in the experimental stage.

useAttrs

Use $attrs in the template to access attrs data. Compared to Vue2, Vue3’s $attrs also contains class and style attributes.

Use the useAttrs function in

<script setup> import HelloWorld from './components/HelloWorld.vue' </script> <template> <HelloWorld class="hello-word" /> </template>Copy the code
/ /. / components/HelloWorld. Vue < script setup > import {useAttrs} from 'vue' const attrs = useAttrs () / / js Console. log(attrs.class) // hello-word console.log(attrs.title) // I am the title </script> <template> <! <div>{{$attrs. Title}}</div> </template>Copy the code

useSlots

Use $slots in the template to access the slots data.

Get the slots data using the useSlots function in

< script setup > import HelloWorld from '. / components/HelloWorld. Vue '< / script > < template > < the HelloWorld > < div > default slot < / div > <template v-slot:footer> <div> Named slot footer</div> </template> </HelloWorld> </template>Copy the code
<script setup> import {useSlots} from 'vue' const slots = useSlots( console.log(slots.default) console.log(slots.footer) </script> <template> <div> <! - the use of a slot in the template - > < slot > < / slot > < slot name = "footer" > < / slot > < / div > < / template >Copy the code

useCssModule

In Vue3, CSS Modules are also supported. Add the module attribute to

The

<script setup lang="ts"> import {useCssModule} from 'vue' Const style = useCssModule() console.log(style.success) // The success class name has been hash calculated Pass the parameter Content, <style module="content"> const contentStyle = useCssModule('content') </script> <template> <div Class ="success"> Normal style red</div> <div :class="$style.success"> Default CssModule pink</div> <div :class="style.success"> Default CssModule pink</div> <div :class=" contentstyle. success"> Named CssModule blue</div> <div :class="content.success"> named CssModule blue</div> </template> <! <style>. Success {color: red; } </style> <! <style module lang="less">. Success {color: pink; } </style> <! <style module="content" lang="less">. Success {color: blue; } </style>Copy the code

Note that the CSS Module with the same name overwrites the previous one.

2.5. Use components

In the Component option, the template needs to use components (except global components) and needs to be registered in the Components option.

In < Script Setup >, components do not need to be registered, and templates can be used directly, which is essentially a top-level variable.

It is recommended to use PascalCase to name and use components.

<script setup>
import HelloWorld from './HelloWorld.vue'
</script>

<template>
  <HelloWorld />
</template>
Copy the code

2.6. The component name

// ./components/HelloWorld.vue
<script>
export default {
  name: 'HelloWorld'
}
</script>

<script setup>
import { ref } from 'vue'
const total = ref(10)
</script>

<template>
  <div>{{ total }}</div>
</template>
Copy the code

Use:

<script setup>
import HelloWorld from './components/HelloWorld.vue'
console.log(HelloWorld.name)  // 'HelloWorld'
</script>

<template>
  <HelloWorld />
</template>
Copy the code

Note: If you set the lang property,

2.7. inheritAttrs

InheritAttrs indicates whether attribute inheritance is disabled. The default value is true.

< script setup > import HelloWorld from '. / components/HelloWorld. Vue '< / script > < template > < the HelloWorld title = "I am the title" / > </template>Copy the code

./components/HelloWorld.vue

<script> export default { name: 'HelloWorld', inheritAttrs: false, } </script> <script setup> import { useAttrs } from 'vue' const attrs = useAttrs() </script> <template> <div> <span <span :title="$attrs.title">hover </span> </span>Copy the code

2.8. Top-level await support

Top-level await can be used in

<script setup>
const userInfo = await fetch(`/api/post/getUserInfo`)
</script>
Copy the code

Note: Async Setup () must be used in combination with Suspense. Suspense is an experimental feature and its API is subject to change at any time, so it is not recommended.

2.9. Namespace components

In VUE3, we can use point syntax to use components mounted on an object.

// components/Form/index.js
import Form from './Form.vue'
import Input from './Input.vue'
import Label from './Label.vue'
// Mount the Input and Label components to the Form component
Form.Input = Input
Form.Label = Label

export default Form
Copy the code
/ / use:  <script setup lang="ts"> import Form from './components/Form' </script> <template> <Form> <Form.Label /> <Form.Input />  </Form> </template>Copy the code

The use of namespace components in another scenario, when importing multiple components from a single file:

// FormComponents/index.js
import Input from './Input.vue'
import Label from './Label.vue'

export default {
    Input,
    Label,
}
Copy the code
Import * as Form from './FormComponents' </script> <template> < form.input > <Form.Label>label</Form.Label> </Form.Input> </template>Copy the code

2.10. State-driven dynamic CSS

The

<script setup> const theme = { color: 'red'} </script> <template> <p>hello</p> </template> <style scoped> p {// Use the top binding color: v-bind('theme. } </style>Copy the code

2.11. Instructions

Global directive:

<template>
  <div v-click-outside />
</template>
Copy the code

Custom instruction:

<script setup> import {ref} from 'vue' const total = ref(10) // Custom directives // Must be named in the format of a small hump beginning with a lowercase v // when used in templates, Do not use vMyDirective const vMyDirective = {beforeMount: beforeMount: (el, binding, vnode) => { el.style.borderColor = 'red' }, updated(el, binding, vnode) { if (el.value % 2 ! == 0) { el.style.borderColor = 'blue' } else { el.style.borderColor = 'red' } }, } const add = () => { total.value++ } </script> <template> <input :value="total" v-my-directive /> <button @click="add">add+1</button> </template>Copy the code

Import directives:

Import {directive as vClickOutside} from 'v-click-outside' </script> <template> <div  v-click-outside /> </template>Copy the code

More about instructions, see the official document (v3.cn.vuejs.org/guide/custo…

2.12. Composition Api Type Constraints

<script setup lang="ts"> import { ref, reactive, computed } from 'vue' type User = { name: string age: Number} // const msg1 = ref('') // Const user1 = ref<User>({name: 'tang', age: Reactive ({}) const user2 = reactive({}) const user2 = reactive({}) const user2 = reactive<User>({}) name: 'tang', age: 18 }) const user4 = reactive({} as User) // computed const msg3 = computed(() => msg1.value) const user5 = computed<User>(() => { return { name: 'tang', age: 18 } }) </script>Copy the code

3. Reference documents

  • sfc-script-setup