shallowRef
ref 的浅层作用形式。shallowRef是浅层引用,只会跟踪原始对象的引用,不会跟踪原始对象的属性
<template>
<div>
<div>{{ data.name }}</div>
<div>{{ data.address.city }}</div>
</div>
</template>
<script setup>
import { shallowRef } from 'vue'
const data = shallowRef({
name: '张三',
age: 18,
address: {
city: '北京'
}
})
// 下面的更新不会触发视图更新
setTimeout(() => {
data.value.name = '李四'
data.value.address.city = '上海'
}, 2000)
// 下面更新会触发视图更新
setTimeout(() => {
data.value = {
name: '王五',
age: 20,
address: {
city: '广州'
}
}
}, 4000)
</script>
triggerRef
强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
<template>
<div>{{ shallow.greet }}</div>
</template>
<script setup>
import { shallowRef, watchEffect} from 'vue'
const shallow = shallowRef({
greet: 'Hello, world'
})
// 触发该副作用第一次应该会打印 "Hello, world"
watchEffect(() => {
console.log('=====>', shallow.value.greet)
})
setTimeout(() => {
// 这次变更数据变了,但视图不会更新
shallow.value.greet = 'Hello, universe'
// 打印 "Hello, universe"
console.log('---->>>', shallow.value.greet)
}, 2000);
</script>
强制更新视图
import { shallowRef, watchEffect, triggerRef} from 'vue'
const shallow = shallowRef({
greet: 'Hello, world'
})
// 触发该副作用第一次应该会打印 "Hello, world"
watchEffect(() => {
console.log('=====>', shallow.value.greet)
})
setTimeout(() => {
shallow.value.greet = 'Hello, universe'
// 更新视图
triggerRef(shallow)
}, 2000);
customRef
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
customRef()
预期接收一个工厂函数作为参数,这个工厂函数接受 track
和 trigger
两个函数作为参数,并返回一个带有 get
和 set
方法的对象。
一般来说,track()
应该在 get()
方法中调用,而 trigger()
应该在 set()
中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
示例
创建一个防抖 ref,即只在最近一次 set 调用后的一段固定间隔后再调用:
import { customRef } from 'vue'
export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
在组件中使用:
<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>
<template>
<input v-model="text" />
</template>
shallowReactive
reactive()的浅层浅层响应式,和reactive()不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。
<template>
<h2>进阶练习</h2>
<div>
<div>姓名:{{ data.name }}</div>
<div>所在城市: {{ data.address.city }}</div>
<div>
<button style="margin-right: 10px" @click="editName">修改姓名</button>
<button @click="editCity">修改城市</button>
</div>
</div>
</template>
<script setup>
import { shallowReactive } from 'vue'
const data = shallowReactive({
name: 'lan',
address: {
city: '广州'
}
})
// 这是响应式,名字修改后视图会更新
const editName = () => {
data.name = 'Alan'
}
const editCity = () => {
// 这是非响应式,修改后视图不会更新,因为它不是根级别
// data.address.city = '中山'
// 但可以这样更新,把整个根给替换
data.address = {
city: '中山'
}
}
</script>
shallowReadonly
readonly()的浅层作用形式,只有根级别是只读不可更改的,根级别以下的可更改,只是视图不会更新,但值改变了
<template>
<h2>进阶练习</h2>
<div>
<div>姓名:{{ data.name }}</div>
<div>所在城市: {{ data.address.city }}</div>
<div>
<button style="margin-right: 10px" @click="editName">修改姓名</button>
<button @click="editCity">修改城市</button>
</div>
</div>
</template>
<script setup>
import { shallowReadonly } from 'vue'
const data = shallowReadonly({
name: 'lan',
address: {
city: '广州'
}
})
// 这里是只读不可更改,修改后视图不会更新值未变
const editName = () => {
data.name = 'Alan'
console.log('data.name', data.name)
}
const editCity = () => {
// 这不是响应式,修改后视图不会更新,但它值确实变了
data.address.city = '中山'
console.log('data.address.city', data.address.city)
}
</script>
<style lang="scss" scoped></style>
toRaw
根据一个 Vue 创建的代理返回其原始对象。这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。(toRaw
是Vue 3 Composition API中的一个函数,它接收一个由reactive
或readonly
方法创建的响应式代理对象,并返回该代理对象对应的原始对象。)
<template>
<h2>进阶练习</h2>
<div>
<div>姓名:{{ data.name }}</div>
<div>所在城市: {{ data.address.city }}</div>
<div>
<button style="margin-right: 10px" @click="editName">修改姓名</button>
</div>
</div>
</template>
<script setup>
import { reactive, toRaw } from 'vue'
const data = reactive({
name: 'lan',
address: {
city: '广州'
}
})
//
const editName = () => {
// 把响应式对象改成普通对象
const rawData = toRaw(data)
rawData.name = 'lanshun' // 修改原始对象的值,页面不会更新
// 输出原始对象
console.log('rawData', rawData)
}
</script>
markRaw
markRaw会将一个对象标记为不可转化为响应式代理对象,并且返回对象本身
<template>
<h2>进阶练习</h2>
<div>
<h2>应用</h2>
<div>姓名: {{ data.name }}</div>
<div>年龄: {{ data.age }}</div>
<div @click="handleChange">修改信息</div>
</div>
<div>
<h2>深入</h2>
<div>姓名: {{ state.name }}</div>
<div>年龄: {{ state.age }}</div>
<div>地址:{{ state.address.city }}</div>
<div @click="handleChange2">修改信息</div>
</div>
</template>
<script setup>
import { reactive, markRaw } from 'vue'
const baseObj = {
name: 'Alan',
age: 23,
job: '码农'
}
const data = markRaw(baseObj)
const handleChange = () => {
data.name = 'Role'
// 操作data.name,发现值改变了,但视图不会更新;是因为markRaw()创建的是非响应式的对象;
console.log('=data==', data)
}
const state = reactive({
name: 'Alan',
age: 23,
job: '码农'
})
// 此时state下的address会变成非响应式对象,不会被追踪,不会更新视图,而其他属性依然是响应式的;
state['address'] = markRaw({ city: '广州' })
const handleChange2 = () => {
state.address.city = '深圳'
console.log('state', state)
}
</script>