生命周期组合式API
setup
立即调用。可以替换之前的beforeCreate以及created,Vue3没有beforeCreate以及created
onMounted()
注册一个回调函数,在组件挂载完成后执行。
组件在以下情况下被视为已挂载
其所有同步子组件都已经被挂载 (不包含异步组件或 <Suspense> 树内的组件)
其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
<script setup>
import { ref, onMounted } from 'vue'
const el = ref()
onMounted(() => {
el.value // <div>
})
</script>
<template>
<div ref="el"></div>
</template>
onBeforeMount()
注册一个钩子,在组件被挂载之前被调用。
function onBeforeMount(callback: () => void): void
当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
onUpdated()
注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用
访问更新后的 DOM
<script setup>
import { ref, onUpdated } from 'vue'
const count = ref(0)
onUpdated(() => {
// 文本内容应该与当前的 `count.value` 一致
console.log(document.getElementById('count').textContent)
})
</script>
<template>
<button id="count" @click="count++">{{ count }}</button>
</template>
onUnmounted()
注册一个回调函数,在组件实例被卸载之后调用。
OnbeforeUpdate --> onBeforeUnmount --> onUnmounted --> onUpdated
一个组件在以下情况下被视为已卸载
其所有子组件都已经被卸载
所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止
可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接
这个钩子在服务器端渲染期间不会被调用
组件被销毁的过程:
<script setup>
import { onMounted, onUnmounted } from 'vue'
let intervalId
onMounted(() => {
intervalId = setInterval(() => {
// ...
})
})
onUnmounted(() => clearInterval(intervalId))
</script>
onBeforeUpdate()
注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
function onBeforeUpdate(callback: () => void): void
onBeforeUnmount()
注册一个钩子,在组件实例被卸载之前调用
OnbeforeUpdate --> onBeforeUnmount --> onUpdated
function onBeforeUnmount(callback: () => void): void
当这个钩子被调用时,组件实例依然还保有全部的功能。
使用案例
- 入口文件:src->App.vue
<template>
<h3>生命周期组合API--->{{count}}</h3>
<button @click="count++">更改count</button>
<hr/>
<button @click="isRender=!isRender">销毁子组件</button>
<Child v-if="isRender"></Child>
</template>
<script lang="ts" setup>
import {onBeforeMount,onUpdated, onBeforeUpdate, onMounted, ref} from "vue";
// 直接将组件引用即可使用,不需要使用components进行注册
import Child from "./components/Child.vue";
const count = ref(100);
const isRender = ref(true);
console.log("立即调用。可以替换之前的beforeCreate以及created");
// 当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。
// 它即将首次执行 DOM 渲染过程
onBeforeMount(function () {
console.group("********onBeforeMount:注册一个钩子,在组件被挂载之前被调用。**************")
console.log("onBeforeMount->count",count.value);// onBeforeMount->count 100
console.log("onBeforeMount->h3",document.querySelector("h3"));// onBeforeMount->h3 null
console.groupEnd();
});
// 注册一个回调函数,在组件挂载完成后执行
onMounted(function () {
console.group("********onMounted:注册一个回调函数,在组件挂载完成后执行。。**************")
console.log("onMounted->count",count.value);// onMounted->count 100
// as 是typescript中的断言语法,指定变量的类型
console.log("onMounted->h3",(document.querySelector("h3") as HTMLHeadingElement).innerText);// onMounted->h3 生命周期组合API--->100
console.groupEnd();
});
// 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onBeforeUpdate(function(){
console.group("********onBeforeUpdate:注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。**************")
console.log("onBeforeUpdate->count",count.value);// onBeforeUpdate->count 101
console.log("onBeforeUpdate->h3",(document.querySelector("h3") as HTMLHeadingElement).innerText);// onBeforeUpdate->h3 生命周期组合API--->100
console.groupEnd();
})
// 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用
onUpdated(function(){
console.group("********onUpdated:注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。。**************")
console.log("onUpdated->count",count.value);// onUpdated->count 101
console.log("onUpdated->h3",(document.querySelector("h3") as HTMLHeadingElement).innerText);// onUpdated->h3 生命周期组合API--->101
console.groupEnd();
})
</script>
- 子组件
<template>
<div>
<h4>Child</h4>
</div>
</template>
<script setup lang="ts">
import {onBeforeUnmount, onBeforeUpdate, onUnmounted} from "vue";
let userName = "zhangpeiyue";
// 注册一个钩子,在组件实例被卸载之前调用 当这个钩子被调用时,组件实例依然还保有全部的功能。
onBeforeUnmount(function(){
console.group("********onBeforeUnmount:注册一个钩子,在组件实例被卸载之前调用。。**************")
console.log("onBeforeUnmount->userName",userName);// zhangpeiyue
console.log("onBeforeUnmount->h3",document.querySelector("h4"));// <h4>Child</h4>
console.groupEnd();
})
// 注册一个回调函数,在组件实例被卸载之后调用
onUnmounted(function(){
console.group("********onUnmounted:注册一个回调函数,在组件实例被卸载之后调用。。**************")
console.log("onUnmounted->userName",userName);// zhangpeiyue
console.log("onUnmounted->h3",document.querySelector("h4"));// null
console.groupEnd();
})
</script>
<style scoped>
</style>
生命周期异步函数
// 生命周期钩子中不写 异步函数,异步函数单独写出来
onMounted(()=>{
getTradeMarkList()
})
// 定义异步函数
const getTradeMarkList = async () => {
await 调用接口
}
computed()
接受一个 getter 函数,返回一个只读的响应式 ref 对象。
该 ref 通过 .value 暴露 getter 函数的返回值。
它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。
类型
// 只读
function computed<T>(
getter: (oldValue: T | undefined) => T,
// 查看下方的 "计算属性调试" 链接
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
// 可写的
function computed<T>(
options: {
get: (oldValue: T | undefined) => T
set: (value: T) => void
},
debuggerOptions?: DebuggerOptions
): Ref<T>
创建一个只读的计算属性ref
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误
创建一个可写的计算属性 ref
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
调试
const plusOne = computed(() => count.value + 1, {
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
使用案例
引入组合API->computed
computed接收一个回调函数
将computed函数的返回结果赋值给常量,变量
模板中可以直接渲染计算属性。
计算属性拥有缓存功能,只有所依赖的数据不发生改变,计算属性对应的函数不会再次执行
模板中如果使用了计算属性,那么在初次渲染时,会执行
- 入口文件:src->App.vue
<template>
<h3>计算属性-只读</h3>
<p @click="count++">count--->{{count}}</p>
<p>userName--->{{userName}}</p>
<p>age--->{{age}}</p>
<p>方案一:函数-->{{getInfo()}}</p>
<p>方案二:拼接-->{{"今年"+userName+","+age+"岁了"}}</p>
<p>方案三:计算属性-->{{getInfoCom}}</p>
<button @click="age++">点我更改age</button>
<button @click="userName+='!'">点我更改userName</button>
<hr/>
<h3>计算属性-读写</h3>
<p @click="count+=2">count-->{{count}}</p>
<p>countCom->{{countCom}}</p>
<button @click="countCom=300">修改计算属性的值</button>
</template>
<script setup lang="ts">
// 引入API
import {ref,computed} from "vue";
const count = ref(100);
const age = ref(1);
const userName = ref("zhangsan");
// getInfo函数的返回结果,只受userName,以及age的影响,
// 但是由于更新了数据count,视图需要更新,由于模板中调用了该函数,所以该函数依然会被执行,哪怕结果没有任何变化。于是需要计算属性,只监测所需要的数据变化时,再执行
// 创建一个普通函数 getInfo函数
const getInfo = function(){
console.log("getInfo");
return "今年"+userName.value+","+age.value+"岁了"
}
// 计算属性:
// 1- 引入组合API->computed
// 2- computed接收一个回调函数
// 3- 将computed函数的返回结果赋值给常量,变量
// 4- 模板中可以直接渲染计算属性。
// 5- 计算属性拥有缓存功能,只有所依赖的数据不发生改变,计算属性对应的函数不会再次执行。
// 6- 模板中如果使用了计算属性,那么在初次渲染时,会执行。
// 创建一个只读的计算属性(参数是一个函数)
const getInfoCom = computed(function(){
console.log("getInfoCom")
return "今年"+userName.value+","+age.value+"岁了"
})
// 创建一个读写的计算属性(参数是一个对象)
const countCom = computed({
// 返回值即是计算属性的值
get(){
return count.value*2;
},
// 当设置计算属性值时调用。接收的参数是修改的值
set(v:number){
count.value = v/2;
}
})
</script>
watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数
watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数
详细信息
第一个参数是侦听器的源。这个来源可以是以下几种
一个函数,返回一个值
一个 ref
一个响应式对象
或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
第三个可选的参数是一个对象,支持以下这些选项:
immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。
flush:调整回调函数的刷新时机。参考回调的刷新时机及 watchEffect()。
onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器。
once:回调函数只会运行一次。侦听器将在回调函数首次运行后自动停止。
与 watchEffect() 相比,watch() 使我们可以:
懒执行副作用;
更加明确是应该由哪个状态触发侦听器重新执行;
可以访问所侦听状态的前一个值和当前值。
类型
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
type WatchCallback<T> = (
value: T,
oldValue: T,
onCleanup: (cleanupFn: () => void) => void
) => void
type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never // 响应式对象
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean // 默认:false
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
once?: boolean // 默认:false (3.4+)
}
侦听一个 getter 函数
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true }
强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
const state = reactive({ count: 0 })
watch(
() => state,
(newValue, oldValue) => {
// newValue === oldValue
},
{ deep: true }
)
当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
const state = reactive({ count: 0 })
watch(state, () => {
/* 深层级变更状态所触发的回调 */
})
watch()
和 watchEffect()
享有相同的刷新时机和调试选项:
watch(source, callback, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
停止侦听器
const stop = watch(source, callback)
// 当已不再需要该侦听器时:
stop()
副作用清理
当
id
变化时,cancel
将被调用,
watch(id, async (newId, oldId, onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 当 `id` 变化时,`cancel` 将被调用,
// 取消之前的未完成的请求
onCleanup(cancel)
data.value = await response
})
使用案例
watch:可以针听一个或多个数据,如果侦听的数据发生变化,会执行指定的函数。
第一个参数是侦听的数据。第二个参数是回调函数。第三个参数是一个配置对象
当侦听的数据发生改变,那么会执行回调函数。
watch的返回值是一个函数,通过调用该函数可以停止侦听(当数据发生改变,那么不会再次执行回调函数)。
// watch:可以针听一个或多个数据,如果侦听的数据发生变化,会执行指定的函数。
import {reactive, ref, watch} from "vue";
// count可以被称为:响应式ref对象|ref对象|ref
const count = ref(1);
// info可以被称为:响应式代理对象|响应式reactive对象|数据源代理对象
const info = reactive({
userName: "zhangsan",
age: 12,
arr: [1, 2, 3, 4]
})
<template>
<h3>watch</h3>
<p>ref->count->{{ count }}</p>
<p>reactive->info->{{ info }}</p>
<button @click="count++">更改ref->count</button>
<br/>
<button @click="info.age++">更改reactive->info->age</button>
<button @click="info.userName+='!'">更改reactive->info->userName</button>
<button @click="info.arr.push(info.arr.length+1)">更改reactive->info->arr</button>
<button @click="info.arr[1]=90">更改reactive->info->arr->下标</button>
<button @click="stopWatch">停止侦听</button>
</template>
<script setup lang="ts">
// watch:可以针听一个或多个数据,如果侦听的数据发生变化,会执行指定的函数。
import {reactive, ref, watch} from "vue";
// count可以被称为:响应式ref对象|ref对象|ref
const count = ref(1);
// info可以被称为:响应式代理对象|响应式reactive对象|数据源代理对象
const info = reactive({
userName: "zhangsan",
age: 12,
arr: [1, 2, 3, 4]
})
// 1- 侦听ref
// 第一个参数是侦听的数据。第二个参数是回调函数。第三个参数是一个配置对象。
// 当侦听的数据发生改变,那么会执行回调函数。
// watch的返回值是一个函数,通过调用该函数可以停止侦听(当数据发生改变,那么不会再次执行回调函数)。
const stopWatch = watch(count,(newValue,oldValue)=> {
console.log("watch", newValue, oldValue)
},{
// 立即调用,如果立即调用那么oldValue的值是undefined
immediate:true
})
// 2- 侦听数据代理对象
const stopWatch = watch(info,(newValue,oldValue)=>{
console.log("侦听数据代理对象",newValue===oldValue) // true 引用地址没有改变
},{
immediate:true
});
// 3- 侦听一个函数,该函数返回一个数据
// 函数要有一个返回值,返回值可以依赖一个或多个数据,当返回值发生改变以后会被侦听到。
const stopWatch = watch(()=>count.value+info.age,(newValue,oldValue)=>{
console.log("侦听一个函数",newValue,oldValue)
},{
immediate:true
})
// 主要应用于侦听代理对象中的某个属性的变化
// 如果返回的是一个引用类型,引用类型的地址没有发生改变不会被侦听,如果被侦听需要增加deep:true
const stopWatch = watch(()=>info.age,(newValue,oldValue)=>{
console.log("侦听一个函数",newValue,oldValue)
},{
// deep:true,
immediate:true
})
// 4- 侦听数组,数组内可以是所有的响应式数据
// newValue是更改后的数据,oldValue是更改前的数据。
// newValue以及oldValue类型是数组。数组中分别为对应数据的更改前以及更改后的值
// info的引用类型的地址没有发生改变,newvalue和oldValue值是一样的。
const stopWatch = watch([info,count],(newValue,oldValue)=>{
// newValue:[info更改后的值,count更改后的值]
// oldValue:[info更改前的值,count更改前的值]
console.log("侦听数组",newValue,oldValue)
},{
immediate:true
})
</script>
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
watchEffect是组合API
watchEffect是一个函数
watchEffect,接收一个回调函数,且该函数会被立即调用
// 特点
1. watchEffect()中的回调函数会立即调用
2. 回调函数中所依赖的数据发生改变,会执行回调函数
类型
function watchEffect(
effect: (onCleanup: OnCleanup) => void,
options?: WatchEffectOptions
): StopHandle
type OnCleanup = (cleanupFn: () => void) => void
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
type StopHandle = () => void
详细信息
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post' 将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync' 来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
返回值是一个用来停止该副作用的函数。
使用
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
副作用清除
清除副作用函数执行的时机
- 当所依赖的数据发生改变会执行清除副作用函数。
- 当停止时执行。
- 当组件被销毁时执行。
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前
// 未完成的请求
onCleanup(cancel)
data.value = await response
})
停止侦听器
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
选项
watchEffect(() => {}, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
使用案例
- 入口文件:src->App.vue
<template>
<h3>watchEffect</h3>
<p @click="count++">count-->{{count}}</p>
<p @click="obj.age++">obj.age-->{{obj.age}}</p>
<hr/>
<button @click="isRender=!isRender">显示与隐藏</button>
<Child v-if="isRender"/>
</template>
<script setup lang="ts">
import Child from "./components/Child.vue";
// 1- watchEffect是组合API
// 2- watchEffect是一个函数
// 3- watchEffect,接收一个回调函数,且该函数会被立即调用
import {onMounted, reactive, ref, watchEffect} from "vue";
const isRender = ref(true);
// 一- 立即调用
watchEffect(function(){
console.log("over");
})
// 二- 回调函数中所依赖的数据发生改变,会执行回调函数。
const count = ref(1);
const obj = reactive({
age:12
})
// 当count发生改变那么会执行
watchEffect(()=>{
console.log(count.value);
})
// 当count或obj.age发生改变,会执行回调函数。
watchEffect(()=>{
console.log(count.value,obj.age);
})
// 三- 清除副作用函数 onInvalidate
// 不需要指定侦听的数据,回调函数的参数是一个函数onInvalidate,onInvalidate函数是为了清理副作用
// 清除副作用函数执行的时机
// 1.当所依赖的数据发生改变会执行清除副作用函数。
// 2.当停止时执行。
// 3.当组件被销毁时执行。
const stop = watchEffect(function(onInvalidate){
console.log(count.value,obj.age);
onInvalidate(function(){
console.log("清理副作用!")
})
})
// 四- 停止侦听器
setTimeout(()=>{
stop();
},5000)
</script>
- 子组件:用于演示 watchEffect 函数的回调函数执行的时机:当停止时执行,当组件被销毁时执行
<template>
<div>Child->{{num}}</div>
</template>
<script setup lang="ts">
import {onBeforeUnmount, onMounted, ref, watchEffect} from "vue";
// 定义一个响应式num
const num = ref(1);
// 方式1:通过onBeforeUnmount钩子函数来执行清除定时器(在组件被销毁前)
// 定义一个定时器标记
let timer = -1;
// 定义一个钩子函数,清除定时器
onBeforeUnmount(function(){
clearInterval(timer);
})
// 立即执行定时器
onMounted(function(){
timer = setInterval(()=>{
num.value++;
console.log(11111)
},1000)
})
// 方式2:通过watchEffect函数的回调函数onInvalidate来执行清除定时器(在组件被销毁前)
// 新建一个侦听器
watchEffect(function(onInvalidate){
console.log("watchEffect回调函数");
// 新建一个多次定时器
const timer = setInterval(function(){
console.log("定时器");
num.value++;
},1000)
onInvalidate(function(){
console.log("onInvalidate");
clearInterval(timer);
})
})
</script>
<style scoped>
</style>
watch与watchEffect异同点
1- watchEffect与watch的初次执行时机不同。
2- watchEffect回调函数所依赖的数据发生改变会调用,watch是指定的侦听数据发生改变才会执行(有第一个参数指定侦听数据)
3- watchEffect无法获取修改前的值。watch可以。
4- watchEffect拥有清理副作用函数。
// 总结:watch与watchEffect有何不同?
// 1- watchEffect与watch的初次执行时机不同。
// 2- watchEffect回调函数所依赖的数据发生改变会调用,watch是指定的侦听数据发生改变才会执行(有第一个参数指定侦听数据)。
// 3- watchEffect无法获取修改前的值。watch可以。
// 4- watchEffect拥有清理副作用函数。
nextTick()
背景:数据发生改变,但是视图并不会立即发生改变
等待下一次 DOM 更新刷新的工具方法。
使用nextTick函数立即渲染更新DOM
setup是在beforecreate钩子函数前执行,而视图是在onUpdated,OnMounted之后才渲染完成,所以在setup阶段获取DOM元素,就需要nextTick()函数立即更新渲染DOM,才能避免undefined的问题。
类型
function nextTick(callback?: () => void): Promise<void>
详细信息
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。
这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。仅执行一次这个onBeforeUpdate钩子函数。
nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。
你可以传递一个回调函数作为参数,或者 await 返回的 Promise。
使用
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
</script>
<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>
使用案例
演示:数据发生改变,但是视图并不会立即发生改变
nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。
<template>
<div>nextTick:{{count}}</div>
<div>nextTick:{{userName}}</div>
<button @click="changeNum">count+1</button>
</template>
<script setup lang="ts">
import {onBeforeUpdate, onUpdated, ref,nextTick} from "vue";
// 定义一个基本数据类型
const count = ref(1);
const userName = ref("zhangsan");
// 定义一个方法
const changeNum = async function(){
count.value++; // 当数据改变时,并不会立即更新视图,而是进入一个队列
userName.value+="!"; // 当所有的异步都执行完之后
// 数据发生改变,但是真实的DOM渲染在此时并没有发生改变
// nextTick:1 nextTick:zhangsan
console.log("changeNum",(document.querySelector("div") as HTMLDivElement).innerText)
// 情况1:使用nextTick函数立即更新DOM,参数是一个回调函数
nextTick(function(){
// 可以立即获取更新以后的视图信息
console.log("nextTick",(document.querySelector("div") as HTMLDivElement).innerText)
})
// 情况2:使用nextTick函数立即更新DOM,作为一个Promise对象
nextTick().then(()=>{
console.log("nextTick",(document.querySelector("div") as HTMLDivElement).innerText)
})
// 情况3:使用nextTick函数立即更新DOM,作为一个异步函数
await nextTick();
console.log("nextTic",(document.querySelector("div") as HTMLDivElement).innerText)
}
// 定义一个更新前钩子
onBeforeUpdate(function(){
console.log("onBeforeUpdate")
})
// 定义一个更新后的钩子
onUpdated(function(){
// 可以获取更新以后的视图信息
console.log("onUpdated",(document.querySelector("div") as HTMLDivElement).innerText);
})
</script>
nextTick应用1
使用nextTick()函数,作为异步函数使用,立即渲染视图
<template>
<button @click="changeIsRender">点我</button>
<div ref="divRef" v-if="isRender"></div>
</template>
<script setup lang="ts">
import {ref,nextTick} from "vue";
const isRender = ref(false);
const divRef = ref();
const changeIsRender = async function(){
// isRender取反
isRender.value = !isRender.value;
// 判断是否渲染
if(isRender.value){
// 立即视图更新
await nextTick();
divRef.value.style.width="300px";
divRef.value.style.height="300px";
divRef.value.style.background = "red";
}
}
</script>
nextTick应用2
ref和v-for结合使用,使用nextTick立即更新视图
<template>
<p ref="pRef" v-for="(item,index) in state.userList" :key="index">{{item}}</p>
</template>
<script setup lang="ts">
import {ref, nextTick, reactive} from "vue";
// 定义一个类型 TState
type TState = {
// 定义userList是一个元素是string类型的数组
userList:string[]
}
// 定义一个Proxy对象
const state = reactive<TState>({
userList: []
});
// 定义一个Proxy对象
const pRef = ref();
// 定义一个定时器
setTimeout(() => {
const data = ["张三", "李四", "王五", "赵六"];
// 把数组赋值给userList,数据发生了改变
state.userList = data;
// 视图立即渲染改变
nextTick(function(){
// ref和v-for结合使用得到一个数组,数组中的元素是DOM元素对象
// 遍历这个数组
pRef.value.forEach((item:HTMLParagraphElement,index:number)=>{
// 设置间隔的元素对象的背景颜色
if(index % 2){
item.style.background = "red";
}else{
item.style.background = "green";
}
})
})
}, 3000)
</script>
nextTick应用3
输入框的编辑和完成操作,并获取光标。
<template>
<div>
<input ref="inputRef" v-if="isEdit" v-model="str" type="text">
<span v-else>{{ str }}</span>
<button @click="onEdit">{{isEdit?"完成":"编辑"}}</button>
</div>
</template>
<script setup lang="ts">
import {ref, nextTick, reactive} from "vue";
let str = ref("你现在过的还好吗?");
let isEdit = ref(false);
let inputRef = ref();
// 定义一个函数
const onEdit = function(){
// 取反
isEdit.value = !isEdit.value;
if(isEdit.value){
// 立即执行回调函数
nextTick(function(){
// 获取光标
inputRef.value.focus();
})
}
}
</script>