目录
[1 shallowRef(浅响应)](#1 shallowRef(浅响应))
[2 shallowReactive(浅响应)](#2 shallowReactive(浅响应))
[3 readonly(只读)](#3 readonly(只读))
[4 shallowReadonly(浅只读)](#4 shallowReadonly(浅只读))
[5 toRaw(还原初始对象)](#5 toRaw(还原初始对象))
[6 markRaw(永不响应)](#6 markRaw(永不响应))
[7 customRef(自定义ref)](#7 customRef(自定义ref))
[8 Teleport(传送门)](#8 Teleport(传送门))
[9 Suspenmse(等待一下)](#9 Suspenmse(等待一下))
[10 补充](#10 补充)
1 shallowRef(浅响应)
基础概念:只监听.vlue的引用变化,如果是数据类型就是这个值,但是如果是对象,就只监控这个对象的引用,对.value内部的对象或者数据类型不做监控。
代码示例:
TypeScript
const state = shallowRef({ count: 1 })
state.value.count++ // 视图不更新 (层级太深)
state.value = { count: 2 } // 视图更新 (替换了整个 .value)
2 shallowReactive(浅响应)
基础概念:只把对象的第一层做响应式的管理,也是监控引用,内部值切换无法监控到,与shallowRef的区别就是他多了一层盒子RefImpl,原理类似。
代码示例:
TypeScript
const state = shallowReactive({ foo: { bar: 1 } })
state.foo = { bar: 2 } // 更新 (第一层)
state.foo.bar++ // 不更新 (第二层)
3 readonly(只读)
基础概念:只读不写,传递的参数都是响应式的,可以防止别人的错误修改。
TypeScript
const original = ref(0)
const copy = readonly(original)
original.value++ // 改原值可以
copy.value++ // 改副本报错:Set operation failed
4 shallowReadonly(浅只读)
基础概念:浅只读,只有第一层不能进行修改,允许深层次的修改。
TypeScript
const state = shallowReadonly({ n: 1, nested: { n: 2 } })
state.n++ // 拦截 (第一层)
state.nested.n++ // Pass (深层未被冻结)
5 toRaw(还原初始对象)
基础概念:toRaw用于获取一个响应式对象的原始对象,相当于是将响应式的代理对象进行拆箱,拆箱成原先的非响应式的对象,一般场景为将响应式对象传递给后端或者外部系统,可以确保他们接收的是普通对象。
TypeScript
const state = reactive({ n: 1 })
const raw = toRaw(state) // 拿到普通对象 { n: 1 }
console.log(raw === state) // false (一个是对象,一个是代理)
6 markRaw(永不响应)
基础概念:给一个对象打上"永不响应"的标记。Vue 见到它就会绕道走,绝不把它变成 Proxy(响应式)。
TypeScript
const foo = markRaw({ complexObj: '地图实例...' })
const state = reactive({ foo })
console.log(isReactive(state.foo)) // false (虽然在 reactive 里,但它免疫)
7 customRef(自定义ref)
shallowRef≈ 一个去掉了递归逻辑的customRef。
readonly≈ 一个set函数只会报错的customRef。
customRef≈ 万能钥匙 。理论上,你可以用customRef手写出ref、shallowRef甚至带防抖功能的 ref,因为它们底层的拦截机制是同源的。
基础概念:创建一个自定义的ref,并对其依赖项追踪赫更新触发进行逻辑控制。
防抖节流:
- track():监控,告诉数据很重要,一旦变化就去更新
- trigger():触发,通知数据发生变化。
代码:延迟一秒更新的hooks代码(自定义ref)
TypeScript
import { customRef } from 'vue'
export function useDelayRef<T>(initialValue: T, delay = 1000) {
let value = initialValue
let timer: number | null = null
return customRef<T>((track, trigger) => {
return {
get() {
track() // 收集依赖
return value
},
set(newValue: T) {
if (timer) clearTimeout(timer)
timer = window.setTimeout(() => {
value = newValue
trigger() // 延迟触发更新
}, delay)
},
}
})
}
8 Teleport(传送门)
基础概念:在逻辑上,组件是父子关系;但在 DOM 渲染 上,将组件的 HTML 结构"传送"到页面的其他位置(如 <body> 下(比如一些弹框))。
代码示例:
父组件
TypeScript
<template>
<div>
<h1>父组件</h1>
<button @click="show = true">打开弹窗</button>
<Modal :isOpen="show" @close="show = false" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import Modal from './Modal.vue'
const show = ref(false)
</script>
子组件: 用 <Teleport to="body"> 把这块 HTML 扔到 body 标签底部。
TypeScript
<template>
<Teleport to="body">
<div v-if="isOpen">
<p>我是弹窗内容</p>
<button @click="$emit('close')">关闭</button>
</div>
</Teleport>
</template>
<script setup>
// 接收状态
defineProps(['isOpen'])
// 声明事件
defineEmits(['close'])
</script>
9 Suspenmse(等待一下)
基础概念:在组件树中协调异步依赖。如果子组件中有 async setup() 或异步组件,它可以显示一个"加载中"的状态(#fallback),等数据都到了再显示真正内容(#default)。
作用:因为子组件有顶层 await,父组件必须使用 <Suspense> 包裹它,否则子组件无法渲染。
TypeScript
<template>
<h3>{{ data }}</h3>
</template>
<script setup>
// 模拟请求延迟 2秒
await new Promise(r => setTimeout(r, 2000))
const data = '✅ 数据请求完成'
</script>
TypeScript
<template>
<h3>{{ data }}</h3>
</template>
<script setup>
// 模拟请求延迟 2秒
await new Promise(r => setTimeout(r, 2000))
const data = '✅ 数据请求完成'
</script>
10 补充
https://v3-migration.vuejs.org/
