<script setup> 是在单文件组件中使用组合式 API 的编译时语法糖,当使用 <script setup> 的时候,任何在其中声明的顶层的绑定 (包括变量
,函数声明
,以及 import 导入的内容
) 都能在模板中直接使用。
<template>
<div><Child></Child>{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child'
const count = ref(0)
</script>
响应式数据
定义响应数据
ref()
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value
reactive()
返回一个对象的响应式代理。
computed()
接受一个 getter 函数,返回一个只读的响应式 ref 对象。
<template>
<span v-show="hidden">{{ count }} </span><button @click="toggle">显示</button>
</template>
<script lang="ts" setup>
import { ref, reactive, computed} from 'vue'
const hidden = ref(false)
const state = reactive({
name: '张三',
age: 18,
family: [
{ name: '张爸', age: 45 },
{ name: '张妈', age: 42 }
]
})
const count = computed(() => {
return state.family.length
})
const toggle = () => {
hidden.value = !hidden.value
}
</script>
监听器
当需要在数据变化时执行一些“副作用”:如更改 DOM、执行异步操作,可以使用监听器。
使用同步语句创建的侦听器当组件卸载的时候会停止监听;使用异步回调创建一个监听器器,必须手动停止,以防内存泄漏。
watch()
watch()用于监听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
参数:
- 监听器的数据源(ref/响应式对象/函数)
- 发生变化时的回调函数
- 配置对象
immediate:在监听器创建时立即触发回调
deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
flush:指定回调函数的刷新时机。- pre (默认)dom 更新前调用回调
- post dom 更新后调用回调
- sync 同步调用回调
onTrack / onTrigger:用于调试的钩子。在依赖收集和回调函数触发时被调用
import { watch } from 'vue'
// 监听ref
watch(hidden, (newValue, oldValue) => {
console.log(`${oldValue} -> ${newValue}`)
})
// 监听proxy对象,vue3将强制开启deep深度监听,和vue2一样,oldValue和newValue相同
watch(state, newValue => {
console.log(newValue)
})
// 使用函数监听proxy数据的某个属性
watch(
() => state.family,
newValue => {
console.log(newValue)
},
{
deep: true // family是引用数据,需开启deep属性
}
)
// 使用数组监听proxy数据的某些属性
watch([() => state.name, () => state.age], ([newName, newAge], [oldName, oldAge]) => {
console.log(`${newName}:${newAge}->${oldName}:${oldAge}`)
})
// 侦听多个数据源
watch([hidden, () => state.name], ([newHidden, newName], [oldHidden, oldName]) => {
console.log(`${newName}:${newHidden}->${oldName}:${oldHidden}`)
})
watchEffect()
watchEffect()会立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行,当组件卸载的时候会停止监听。
参数:
- 要运行的副作用函数
- 一个可选的选项
flush: 指定回调函数的刷新时机
onTrack / onTrigger:用于调试的钩子。在依赖收集和回调函数触发时被调用
import { watchEffect } from 'vue'
watchEffect(()=>{
console.log("watchEffect配置的回调执行了",hidden.value)
})
停止监听
const stop=watch(hidden, (newValue, oldValue) => {
console.log(`${oldValue} -> ${newValue}`)
})
setTimeout(()=>{
stop()
}, 3000)
组件传值
defineProps() / defineEmits()
- defineProps(): 子组件使用defineProps接收父组件的props
- defineEmits(): 子级组件向父级组件传值
子组件
<template>
<span >{{ props.name }} </span>
<input type="text" @change="changeName" v-model="name">
</template>
<script setup>
import { ref} from 'vue'
const props = defineProps({
name: { type: String, required: true }
})
const emit = defineEmits(['updateName'])
const name= ref(props.name)
const changeName = () => {
emit('updateName', name.value)
}
</script>
父组件
<template>
<Child :name="name" @updateName="handleUpdate" />
</template>
<script setup>
import { ref} from 'vue'
import Child from './components/Child.vue'
const name=ref('张三')
const handleUpdate=(newName)=>{
console.log(newName)
name.value=newName
}
</script>
模板ref / defineExpose()
- 模板ref 和选项式api一样,在模板中用ref 注册元素或子组件的引用
- defineExpose():script setup语法糖不能直接使用ref 取到子组件的方法和变量,需使用defineExpose方法暴露组件的属性以及方法供外部使用。
子组件
<template>
<input type="text" @change="changeName" v-model="name">
</template>
<script setup>
import { ref,defineExpose } from 'vue'
const emit = defineEmits(['updateName'])
const name= ref('张三')
const changeName = () => {
emit('updateName', name.value)
}
// 将方法、变量暴露给父组件使用,父组件才可通过ref API拿到子组件暴露的数据
defineExpose({
name,
changeName
})
</script>
父组件
<template>
<Child ref="root" />
</template>
<script setup>
import { ref,onMounted } from 'vue'
import Child from './components/Child.vue'
const root = ref(null)
onMounted(() => {
console.log(root.value) // DOM 元素将在初始渲染后分配给 ref
})
</script>
provide() / inject()
依赖注入
- provide()
提供一个值,可以被后代组件注入,provide()接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。 - inject()
注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值
父组件
import { ref,provide } from 'vue'
// 提供静态值
provide('size','small')
// 提供响应式的值
const loading = ref(false)
provide('loading', loading)
后端组件
import { inject } from 'vue'
const size = inject('size')
const loading = inject(loading)
插槽
- 在子组件的模板中使用<slot>元素定义插槽出口
<template>
<div>
<slot name="title"></slot>
<slot name="content" msg="子组件的数据"></slot>
<slot></slot>
</div>
</template>
- 在父组件的模板中使用v-slot:(缩写:#)指令声明具名插槽或是期望接收 props 的作用域插槽.
<template>
<Child>
<template v-slot:title>标题</template>
<template #content="{ msg }">内容:{{msg}}</template>
<span>作者:admin</span>
</Child>
</template>
useSlots() 和 useAttrs()
import { useSlots, useAttrs } from 'vue'
// 获取插槽的信息
const slots = useSlots()
// 获取过来的所有属性
const attrs = useAttrs()
常用生命周期钩子
钩子函数 | 生命周期选项 | 执行条件 | 备注 |
---|---|---|---|
onBeforeMount() | beforeMount | 在组件被挂载之前被调用 | |
onMounted() | mounted | 在组件挂载完成后执行 | |
onBeforeUpdate() | beforeUpdate | 在组件即将因为响应式状态变更而更新其 DOM 树之前调用 | |
onUpdated() | updated | 在组件因为响应式状态变更而更新其 DOM 树之后调用 | |
onBeforeUnmount | beforeDestroy | 在组件实例被卸载之前调用 | |
onUnmounted() | destroyed | 在组件实例被卸载之后调用 | |
onErrorCaptured() | errorCaptured | 在捕获了后代组件传递的错误时调用 |
顶层 await
await setup前面会自动加一个async
const data = await fetch("/api/demo")
使用路由
import { useRoute, useRouter } from 'vue-router'
const route = useRoute() // 返回当前路由地址
const router = useRouter() // 返回 router 实例
import { nextTick } from 'vue'
// DOM更新完毕之后执行回调的两种写法
nextTick(() => {
console.log('DOM更新完毕')
})
await nextTick()
console.log('DOM更新完毕')