1. Введение
В этой статье в основном объясняется<script setup> а такжеTypeScriptбазовое использование.
<script setup>что это такое?
<script setup>используется в однофайловом компоненте (SFC)composition apiсинтаксический сахар времени компиляции.
На момент написания этой статьи,vueв использовании3.2.26Версия.
1.1 История развития
давайте сначала посмотримvue3 <script setup> История развития:
-
Vue3В более ранних версиях (3.0.0-beta.21перед) средняя параcomposition apiподдержка доступна только в опциях компонентаsetupиспользуется в функции.
<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>
-
существует3.0.0-beta.21добавлена версия
<script setup>экспериментальные свойства. Если вы используете его, вам будет предложено<script setup>Все еще в экспериментальной стадии. -
существует3.2.0удалено из версии
<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>
<template>
<h1>{{ msg }}</h1>
<button type="button" @click="add">count is: {{ count }}</button>
<ComponentA />
<ComponentB />
</template>
1.2 Преимущества
с вариантами компонентовsetupсравнение функций,<script setup>Преимущества:
- Меньше, чище код, не нужно использовать
return {}Доступны переменные и методы, и при использовании компонентов не требуется активная регистрация; - лучше один
TypeScriptПоддержка, используя чистыеTypeScriptутверждениеpropsи кидать события, уже не нравитсяoption apiтакой хромой; - лучшая производительность во время выполнения;
Конечно,<script setup>Он также имеет свои недостатки, такие как необходимость изучения дополнительныхAPI.
Так<script setup>Как это использовать? Каковы точки использования? Как совместить с TypeScript?
2. Используйте баллы
2.1 Инструменты
Vue3Однофайловый компонент (SFC)TS IDEПожалуйста, используйте<script setup lang="ts"> + VSCode + Volar.
Проверка типа с помощьюvue-tscЗаказ.
-
VSCode: лучший интерфейс
IDE, этоTypeScriptОбеспечивает хорошую поддержку из коробки. -
Volar:для
Vue3из*.vueОднофайловые компоненты обеспечивают подсветку кода, подсказки по синтаксису и другие поддерживаемые функции.VSCodeплагин;Vue2вы можете использоватьVeturплагин, который нужно отключитьVetur,скачатьVolarи включите его. -
vue-tsc: проверка типов и
dtsСоздавайте инструменты командной строки.
2.2. Основное использование
Будуsetupатрибут добавлен к<script>на кодовом блоке.
<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>
Используйте при необходимостиTypeScript, тоlangатрибут добавлен к<script>кодовый блок и назначить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>Скрипты в блоках компилируются в опции компонентовsetupСодержимое функции, то есть она будет выполняться каждый раз при создании экземпляра компонента.
существует<script setup>Объявленные привязки верхнего уровня (переменные, функции и содержимое, введенное при импорте) автоматически отображаются в шаблоне и используются непосредственно в шаблоне.
<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>
Уведомление:
-
каждый
*.vueФайл может содержать не более одного<script>блокировать (исключая<script setup>); -
каждый
*.vueФайл может содержать не более одного<script setup>блокировать (кроме обычных<script>);
2.3 Макросы компилятора
макросы компилятораимеют:defineProps,defineEmits,withDefaults,defineExposeЖдать.
макрос компилятораТолькосуществует<script setup> используется в блоке, не требует импорта и будет обрабатываться<script setup>блоки компилируются вместе.
макрос компиляторадолженсуществует<script setup> используется на верхнем уровне<script setup> ссылаются на локальные переменные.
defineProps
существует<script setup> В блоке нет элемента конфигурации компонента, то есть нетpropsвариант, нужно использоватьdefinePropsобъявитьpropsСвязанная информация.definePropsПолученные параметры объекта и компонентаpropsтакое же значение.
<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>
ТС версия:
<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>
можно узнать из кодаTSнаписать фариpropsЗначение по умолчанию не определено.
Vue3предоставляет намwithDefaultsЭтот макрос компилятора даетpropsУкажите значения по умолчанию.
<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>
Одно предостережение: объявите иpropsатрибуты с тем же именем, что и переменные, будут некоторые проблемы.
<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>
Итак, как и в случае с параметрами компонентов, не определяйте иpropsСвойство переменной верхнего уровня с тем же именем.
defineEmits
то же, в<script setup> В блоке также отсутствует элемент конфигурации компонентаemitsДа, нужно использоватьdefineEmitsОбъявление макроса компилятораemitsСвязанная информация.
// ./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>
Используйте компоненты:
<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>
ТС версия:
// ./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>
Используйте компоненты:
<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
существуетVue3 , по умолчанию не будет показывать<script setup> Привязки объявлены в , т.е. недоступны через шаблоныrefПолучите привязку к объявлению экземпляра компонента.
Vue3при условииdefineExposeМакросы компилятора, которые явно предоставляют переменные и методы, объявленные в компонентах, которые необходимо предоставить.
// ./components/HelloWorld.vue
<script setup>
import { ref } from 'vue'
const msg = ref('Hello Vue3')
const handleChangeMsg = (v) => {
msg.value = v
}
// 对外暴露的属性
defineExpose({
msg,
handleChangeMsg,
})
</script>
<template>
<h1>{{ msg }}</h1>
</template>
Используйте компоненты:
<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>
ТС версия:
// ./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>
Используйте компоненты:
<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 Вспомогательные функции
существует<script setup>Часто используемые вспомогательные функции вhooks api,Есть:useAttrs,useSlots,useCssModule, другие вспомогательные функции пока находятся в экспериментальной стадии и внедряться не будут.
useAttrs
использовать в шаблоне$attrsпосетитьattrsДанные иVue2в сравнении с,Vue3из$attrsтакже содержитclassа такжеstyleАтрибуты.
существует<script setup>используется вuseAttrsприобретение функцииattrsданные.
<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
использовать в шаблоне$slotsпосетитьslotsданные.
существует<script setup>используется вuseSlotsприобретение функцииslotsданные слота.
<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
существуетVue3, также поддерживаетCSS ModulesДа, в<style> увеличиватьmoduleсвойства, то есть<style module> .
<style module>Блок кода будет скомпилирован какCSS Modulesи иметь генерируемый класс CSS как$styleКлюч объекта предоставляется компоненту и может использоваться непосредственно в шаблоне.$style. А для таких как<style module="content">названныйCSS Modules, скомпилированные классы CSS генерируются какcontentКлючи объекта доступны компоненту, т.е.moduleКакова стоимость свойства, какой объект выставляется.
<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>
ПРИМЕЧАНИЕ, CSS Time Имя Модуль, за переопределением ранее.
2.5 Использование компонентов
В параметрах компонента шаблон должен использовать компоненты (кроме глобальных компонентов), которые должны находиться вcomponentsвариант зарегистрироваться.
пока в<script setup>Средний компонент не требует повторной регистрации, а шаблон можно использовать напрямую, что фактически эквивалентно переменной верхнего уровня.
Рекомендуется использовать PascalCase для именования и использования компонентов, чтобы отличать их от обычных тегов.
<script setup>
import HelloWorld from './HelloWorld.vue'
</script>
<template>
<HelloWorld />
</template>
2.6. Установите имя компонента
<script setup>не является элементом конфигурации компонентаname, вы можете использовать общий<script>настроить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>
использовать:
<script setup>
import HelloWorld from './components/HelloWorld.vue'
console.log(HelloWorld.name) // 'HelloWorld'
</script>
<template>
<HelloWorld />
</template>
Примечание: если вы установитеlangАтрибуты,<script setup> а также<script>изlangНужно быть последовательным.
2.7. Установка атрибутов наследования
inheritAttrsУказывает, следует ли отключить наследование свойств. Значение по умолчанию —true.
<script setup>Элемент конфигурации компонента inheritAttrs отсутствует, вы можете использовать обычный<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. Поддержка ожидания на высшем уровне
<script setup>Вы можете использовать ожидание верхнего уровня в . Полученный код будет скомпилирован вasync setup()
<script setup>
const userInfo = await fetch(`/api/post/getUserInfo`)
</script>
Уведомление:async setup()необходимо сочетать сSuspenseиспользуется в сочетании,SuspenseЭта функция все еще находится в экспериментальной стадии, и ее API может измениться в любое время, поэтому пока ее не рекомендуется использовать.
2.9 Компоненты пространства имен
существуетvue3, мы можем использовать точечную нотацию для использования компонентов, смонтированных на объекте.
// 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>
Использование компонентов пространства имен в другом сценарии при импорте нескольких компонентов из одного файла:
// 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. Динамический CSS, управляемый состоянием
Vue3середина<style>Этикетки можно пропускатьv-bindЭта функция CSS связывает значения CSS с динамическими состояниями компонентов.
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
// 使用顶层绑定
color: v-bind('theme.color');
}
</style>
2.11 Использование директив
Глобальная директива:
<template>
<div v-click-outside>tang</div>
</template>
Пользовательская директива:
<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>
Импортированная директива:
<script setup>
// 导入的指令同样需要满足命名规范
import { directive as vClickOutside } from 'v-click-outside'
</script>
<template>
<div v-click-outside>tang</div>
</template>
Для получения дополнительной информации о директивах см. официальную документацию (V3. Talent.v UE JS.org/expensive/подскажите ему о…
2.12 Ограничения типа API композиции
<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>
2.13 Получить экземпляр компонента
существуетvue2, мы используемthisполучить доступ к экземпляру компонента, вvue3изsetup()вариант или<script setup>синтаксический сахар неthis, мы можем использоватьgetCurrentInstanceфункция для получения экземпляра текущего компонента.
<script setup>
import { getCurrentInstance, ref, onMounted } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
// 获取当前组件的实例对象,相当于 vue2 的 this
const instance = getCurrentInstance()
console.log(instance)
// 获取 HelloWorld 组件实例
const helloWorld = ref(null)
onMounted(() => {
console.log(instance.$el)
console.log(helloWorld.value.$el)
helloWorld.value.$el.setAttribute('data-name', '定义name');
})
</script>
<template>
<div>标题</div>
<HelloWorld title="11" ref="helloWorld" />
</template>
Уведомление:
- Пожалуйстане хочуДумайте об этом как о получении его в составном API
thisальтернатива использованию. -
getCurrentInstanceТолькосуществуетsetupили крючок жизненного цикла.