Vue3 <script setup lang="ts"> usage guide

This article mainly explains  the basic use of <script setup> and  TypeScript .

<script setup> what is it

<script setup>composition api is compile-time syntactic sugar for  use in single-file components (SFCs)  .

At the time of writing this article, version 3.2.26vue  was used   .

1.1. Development history

Let's take a look  vue3 <script setup> at the development history:

  • Vue3 Supported in earlier (  3.0.0-beta.21 before)  versions composition api , can only be used in component options  setup functions.
<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 {} 把变量、方法暴露给模板

return {

count,

add,

}

},

})

</script>

  •  Experimental feature  added in version  3.0.0  -beta.21 . <script setup>If you use it, you will be prompted that it  <script setup> is still in the stage of experimental features.

  •  The experimental state that was removed in version 3.2.0 has since then been officially declared  to   be  in  regular use and has become one of the stable features of the framework.<script setup><script setup>

<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>x
<template>

<h1>{
   
   { msg }}</h1>

<button type="button" @click="add">count is: {
   
   { count }}</button>

<ComponentA />

<ComponentB />

</template>

1.2. Advantages

 Advantages compared to component option  setup functions  :<script setup>

  • Less and more concise code, no need to use  return {} exposed variables and methods, no need to actively register when using components;
  • Better  Typescript support, using pure  Typescript declarations  props and throwing events, no longer as  option api lame as in;
  • better runtime performance;

Of course,  <script setup> it also has its own shortcomings, such as the need to learn additional  API.

So  <script setup> how to use it? What are the points of use? How to combine with TypeScript?

2. Points of use

2.1. Tools

Vue3 Support for Single File Components (SFC)  TS IDE please use  <script setup lang="ts"> + VSCode + Volar.

Type checking uses  vue-tsc the command.

  • VSCode : the best front-end  IDE.
  • Volar : A plug-in that  Vue3 provides  *.vue support for code highlighting, syntax hints, etc. for single-file components  VSCode ; Vue2 you may be using  Vetur a plug-in that needs to be disabled  Vetur, downloaded  Volar, and enabled.
  • vue-tsc : Type checking and  dts build command line tool.

2.2. Basic usage

Add  setup attributes to  <script> code blocks.

<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>

If you need to use it  TypeScriptlang add the attribute to  <script> the code block and assign it a value  ts.

<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>

<script setup> The script in the block will be compiled into  setup the content of the component options function, which means it will be executed every time the component instance is created.

The top-level bindings of  <script setup> the declaration (variables, functions, and content introduced by import) will be 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>

Notice:

  • Each  *.vue file can contain at most one  <script> block at a time (exclusive <script setup>);

  • Each  *.vue file can contain at most one  <script setup> block at a time (excluding regular ones  <script>);

2.3. Compiler macros

Compiler macros (compiler macros)  are: defineProps, defineEmits, withDefaults, defineExpose etc.

Compiler macros can only<script setup>  be used in blocks, do not need to be imported, and will  be  <script setup> compiled out when the block is processed.

Compiler macros must be used at  <script setup> the top level of , and cannot be  <script setup> referenced in local variables of .

defineProps

<script setup> There is no component configuration item in the block, that is to say, there is no  option  props , which needs to be used  defineProps to declare  props related information. defineProps The object received  props is the same as the value of the component option.

<script setup>

const props = defineProps({

msg: String,

title: {

type: String,

default: '我是标题'

},

list: {

type: Array,

default: () => []

}

})



// 在 js 中使用 props 中的属性

console.log(props.msg)

</script>



<template>

<!-- 在模板中直接使用 props 中声明的变量 -->

<h1>{
   
   { msg }}</h1>

<div>{
   
   { title }}</div>

</template>

TS version:

<script setup lang="ts">

interface ListItem {

name: string

age: number

}

const props = defineProps<{

msg: string

title: string

list: ListItem[]

}>()



// 在 ts 中使用 props 中的属性,具有很好的类型推断能力

console.log(props.list[0].age)

</script>



<template>

<h1>{
   
   { msg }}</h1>

<div>{
   
   { title }}</div>

</template>

From the code, it can be found that   there is no default value defined TS in the writing method  .props

Vue3withDefaults This compiler macro   is provided for us  to props provide default values.

<script setup lang="ts">

interface ListItem {

name: string

age: number

}

interface Props {

msg: string

// title可选

title?: string

list: ListItem[]

}

// withDefaults 的第二个参数便是默认参数设置,会被编译为运行时 props 的 default 选项

const props = withDefaults(defineProps<Props>(), {

title: '我是标题',

// 对于array、object需要使用函数,和以前的写法一样

list: () => []

})

// 在 ts 中使用 props 中的属性,具有很好的类型推断能力

console.log(props.list[0].age)

</script>



<template>

<h1>{
   
   { msg }}</h1>

<div>{
   
   { title }}</div>

</template>

One caveat: declaring a propsvariable with the same name as the property at the top level can be problematic.

<script setup>

const props = defineProps({

title: {

type: String,

default: '我是标题'

}

})

// 在顶层声明一个和props的属性title同名的变量

const title = '123'



</script>



<template>

<!-- props.title 显示的是 props.title 的值,‘我是标题’ -->

<div>{
   
   { props.title }}</div>

<!-- title 显示的是 在顶层声明的 title 的值,‘123’ -->

<div>{
   
   { title }}</div>

</template>

So, as with component options, don't define  props top-level variables with the same name as the property.

defineEmits

Similarly,  <script setup> there is no component configuration item in the block  emits , and  defineEmits compiler macros need to be used to declare  emits related information.

// ./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>

Use 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>

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>

Use 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>

defineExpose

In  Vue3, any  <script setup> binding declared in is not exposed by default, that is,  ref the binding declared by the component instance cannot be obtained through the template.

Vue3 Compiler macros are provided  defineExpose to explicitly expose variables and methods declared in components that need to be exposed.

// ./components/HelloWorld.vue

<script setup>

import { ref } from 'vue'

const msg = ref('Hello Vue3')



const handleChangeMsg = (v) => {

msg.value = v

}

// 对外暴露的属性

defineExpose({

msg,

handleChangeMsg,

})

</script>

Use 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>

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>

Use components:

<script setup lang="ts">

import { ref, onMounted } from 'vue'

import HelloWorld from './components/HelloWorld.vue'

// 此处暂时使用any,需要定义类型

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>

2.4. Helper functions

<script setup> The auxiliary functions commonly used in hooks apimainly include: useAttrs, useSlots, useCssModule, and other auxiliary functions are still in the experimental stage, and will not be introduced.

useAttrs

Used in templates  $attrs to access  attrs data,  Vue2 compared to , Vue3 which  $attrs also contains  class and  style attributes.

<script setup> Use  useAttrs the function in to get  the  attrs data.

<script setup>

import HelloWorld from './components/HelloWorld.vue'

</script>



<template>

<HelloWorld class="hello-word" title="我是标题" />

</template>
// ./components/HelloWorld.vue

<script setup>

import { useAttrs } from 'vue'



const attrs = useAttrs()

// js中使用

console.log(attrs.class) // hello-word

console.log(attrs.title) // 我是标题

</script>



<template>

<!-- 在模板中使用 $attrs 访问属性 -->

<div>{
   
   { $attrs.title }}</div>

</template>
 
 

useSlots

Used in templates  $slots to access  slots data.

<script setup> Use  useSlots the function in to get  the  slots slot data.

<script setup>

import HelloWorld from './components/HelloWorld.vue'

</script>



<template>

<HelloWorld>

<div>默认插槽</div>

<template v-slot:footer>

<div>具名插槽footer</div>

</template>

</HelloWorld>

</template>

<script setup>

import { useSlots } from 'vue'



const slots = useSlots()

// 在js中访问插槽默认插槽default、具名插槽footer

console.log(slots.default)

console.log(slots.footer)

</script>



<template>

<div>

<!-- 在模板中使用插槽 -->

<slot></slot>

<slot name="footer"></slot>

</div>

</template>

useCssModule

In  Vue3 , it is also supported  CSS Modules ,  <style> adding  module attributes on , ie <style module>.

<style module> Code blocks are compiled to  CSS Modules and the generated CSS classes  $style are exposed to components as object keys, which can be used directly in templates  $style. As for  <style module="content"> named  CSS Modules, the CSS class generated after compilation  content is exposed to the component as the key of the object, that is, module whatever the property value is, the object will be exposed.

<script setup lang="ts">

import { useCssModule } from 'vue'



// 不传递参数,获取<style module>代码块编译后的css类对象

const style = useCssModule()

console.log(style.success) // 获取到的是success类名经过 hash 计算后的类名



// 传递参数content,获取<style module="content">代码块编译后的css类对象

const contentStyle = useCssModule('content')

</script>



<template>

<div class="success">普通style red</div>



<div :class="$style.success">默认CssModule pink</div>

<div :class="style.success">默认CssModule pink</div>



<div :class="contentStyle.success">具名CssModule blue</div>

<div :class="content.success">具名CssModule blue</div>

</template>



<!-- 普通style -->

<style>

.success {

color: red;

}

</style>



<!-- 无值的css module -->

<style module lang="less">

.success {

color: pink;

}

</style>



<!-- 具名的css module -->

<style module="content" lang="less">

.success {

color: blue;

}

</style>

Note that for CSS Modules with the same name, the later ones will overwrite the former ones.

2.5. Using components

In component options, templates need to use components (except global components), which need to  components be registered in the options.

However,  <script setup> the component does not need to be registered again, and the template can be used directly, which is actually equivalent to a top-level variable.

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

<script setup>

import HelloWorld from './HelloWorld.vue'

</script>



<template>

<HelloWorld />

</template>

2.6. Component name

<script setup> There is no component configuration item  name , you can use an ordinary one  <script> to configure it  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>

use:

<script setup>

import HelloWorld from './components/HelloWorld.vue'

console.log(HelloWorld.name) // 'HelloWorld'

</script>



<template>

<HelloWorld />

</template>

Note: If you set  lang properties,  be consistent <script setup> with  <script> your  needs.lang

2.7. inheritAttrs

inheritAttrs Indicates whether to disable attribute inheritance, the default value is  true.

<script setup> There is no component configuration item inheritAttrs, you can use an ordinary one  <script>.

<script setup>

import HelloWorld from './components/HelloWorld.vue'

</script>



<template>

<HelloWorld title="我是title"/>

</template>

./components/HelloWorld.vue

<script>

export default {

name: 'HelloWorld',

inheritAttrs: false,

}

</script>



<script setup>

import { useAttrs } from 'vue'

const attrs = useAttrs()

</script>



<template>

<div>

<span :title="attrs.title">hover一下看title</span>

<span :title="$attrs.title">hover一下看title</span>

</div>

</template>

2.8. Top-level await support

<script setup> You can use top-level await in . The resulting code will be compiled into async setup()

<script setup>

const userInfo = await fetch(`/api/post/getUserInfo`)

</script>

Note: async setup() It must be  Suspense used in combination with . Suspense It is still in the experimental stage, and its API may change at any time. It is recommended not to use it for the time being.

2.9. Namespace components

In  vue3 , we can use dot 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'

// 把Input、Label组件挂载到 Form 组件上

Form.Input = Input

Form.Label = Label



export default Form




// 使用:

<script setup lang="ts">

import Form from './components/Form'

</script>



<template>

<Form>

<Form.Label />

<Form.Input />

</Form>

</template>
/

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,

}




// 使用

<script setup>

import * as Form from './FormComponents'

</script>



<template>

<Form.Input>

<Form.Label>label</Form.Label>

</Form.Input>

</template>

 

2.10. State-driven dynamic CSS

Vue3 The middle  <style> tag can  v-bind associate the CSS value with the dynamic component state through this CSS function.

<script setup>

const theme = {

color: 'red'

}

</script>



<template>

<p>hello</p>

</template>



<style scoped>

p {

// 使用顶层绑定

color: v-bind('theme.color');

}

</style>

2.11. Commands

Global directives:

 
 
<template>
<div v-click-outside />
</template>

Custom directive:

<script setup>

import { ref } from 'vue'

const total = ref(10)



// 自定义指令

// 必须以 小写字母v开头的小驼峰 的格式来命名本地自定义指令

// 在模板中使用时,需要用中划线的格式表示,不可直接使用vMyDirective

const vMyDirective = {

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>

Imported directives:

<script setup>

// 导入的指令同样需要满足命名规范

import { directive as vClickOutside } from 'v-click-outside'

</script>



<template>

<div v-click-outside />

</template>

For more information about directives, see official documents ( https://v3.cn.vuejs.org/guide/custom-directive.html#%E7%AE%80%E4%BB%8B, https://v3.cn. vuejs.org/api/application-api.html#directive ).

2.12. Composition Api Type Constraints

<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
 
type User = { 
  name: string
  age: number
}
 
// ref
const msg1 = ref('')  //  会默认约束成 string 类型,因为ts类型推导
const msg2 = ref<string>('')  //  可以通过范型约束类型
const user1 = ref<User>({ name: 'tang', age: 18 })  //  范型约束
const user2 = ref({} as User)  // 类型断言
 
// reactive
const obj = reactive({})
const user3 = 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>

Guess you like

Origin blog.csdn.net/qq_27318177/article/details/126399028