Proxy
Proxy
可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
返回的是一个代理对象,以后的操作都通过代理对象进行。这样就可以在每次访问和赋值的时候进行某些操作
是深度拦截的:嵌套的对象,新建的属性等都会进行代理
Vue
中当使用 Composition API
显式创建响应式对象时,最佳做法是不要保留对原始对象的引用,而只使用响应式版本
Proxy
基本用法
const obj = {
name: 'wmm',
age: 18
}
const proxy = new Proxy(obj, {
get(target, key) {
console.log('get: ', key)
return target[key]
},
set(target, key, value) {
console.log('set: ', key, value)
target[key] = value
}
})
// 读取或者写入的时候,使用代理对象
// 使用原始对象的话,不会打印出 get: 或者 set:
console.log(proxy.name) // wmm
// 原对象和代理对象是不一样的
console.log(proxy === obj) // false
proxy.name = 'wmm66'
console.log(obj) // { name: 'wmm66', age: 18 }
使用Reflect
的方法,可以让我们正确的执行this
绑定
在proxy
中的getter
中跟踪更改它的函数
在proxy
中的setter
中触发函数trigger
以便视图等可以更新最终值
const obj = {
name: 'wmm',
age: 18
}
const proxy = new Proxy(obj, {
get(target, key) {
// 跟踪更改它的函数
track(target, key)
return Reflect.get(...arguments)
},
set(target, key, value) {
// 触发函数以便它可以更新最终值
trigger(target, key)
return Reflect.set(...arguments)
}
})
Vue
在内部跟踪所有已被设置为响应式的对象,因此它始终会返回同一个对象的 proxy
版本。
从响应式 proxy
访问嵌套对象时,该对象在返回之前也被转换为 proxy
const handler = {
get(target, prop, receiver) {
track(target, prop)
const value = Reflect.get(...arguments)
if (isObject(value)) {
return reactive(value)
} else {
return value
}
}
// ...
}
常用API
ref(value)
:将值value
转成响应式对象reactive(obj)
:返回对象的响应式副本readonly(obj | proxy)
:返回一个只读的proxy
对象isProxy(proxy)
:对象是否是由reactive
或readonly
创建的proxy
isReactive(proxy)
:对象是否是reactive
创建的响应式proxy
isReadonly(proxy)
:对象是否是由readonly
创建的只读proxy
isRef(proxy)
:是否是ref
对象toRef(proxy, key)
:为原响应式对象上的property
新创建一个ref
。保持对其源property
的响应式连接toRefs(proxy)
:将响应式对象转换为普通对象。结果对象的每个property
都是指向原始对象相应property
的ref
。常用于批量解构并保持响应性
ref
有一个独立的原始值,把它变成响应式的,可以使用ref()
返回一个可变的响应式对象,该对象包含一个名为value
的属性。访问和修改值可以借助该属性
<template>
<div>
<!-- 直接访问name即可,setup中的ref会自动展开。不是 name.value -->
{{ name }} <button @click="nameAdd">修改name值</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const name = ref('wmm')
// 响应式的
console.log(name)
// 读取值(访问.value属性)
console.log(name.value) // wmm
const nameAdd = () => {
// 设置值(修改.value属性的值)
name.value += '6'
}
return {
// return出去的需要是响应式的数据,这样数据有修改,视图才能对应的更新
// name是响应式的, name.value不是响应式的(只是一个字符串)
// 所以这里应该是 name: name 不能是 name: name.value
name, // 相当于 name: name
nameAdd
}
}
}
</script>
reactive
相当于vue 2.x
的Vue.observable()
返回一个响应式的proxy
对象
深度转换,影响嵌套对象传递的所有property
组件中的data()
返回一个对象时,在内部就是交由reactive()
使其成为响应式对象的
<template>
<div>
{{ person.name }} <button @click="nameAdd">修改name值</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
const obj = {
name: 'wmm',
age: 18
}
// 返回的是一个代理对象。是一个响应式对象
const person = reactive(obj)
console.log(person)
// 代理对象跟原对象不同。一般用的是代理对象,代理对象是响应式的
console.log(person === obj) // false
console.log(person.name) // wmm
person.age++
// 修改了代理对象,原对象也会改变
console.log(person.age, obj.age) // 19 19
const nameAdd = () => {
person.name += '6'
}
return {
person,
nameAdd
}
}
}
</script>
ref + reactive
import { ref, reactive } from 'vue'
export default {
setup() {
const name = ref('wmm')
const job = ref('fe')
const fav = ref('basketball')
const person = reactive({
name,
age: 18
})
person.job = job // .job 是一个响应式对象 RefImpl
person.fav = fav.value // .fav 是一个字符串
console.log(person)
person.name += 66
person.job += 66
person.fav += 66
console.log(person.name, name.value) // wmm66 wmm66
console.log(person.job, job.value) // fe66 fe66
// .fav只是一个字符串,是基本数据类型
console.log(person.fav, fav.value) // basketball66 basketball
}
}
Ref
展开仅发生在被响应式 Object
嵌套的时候
当从 Array
或原生集合类型如Map
访问 ref
时,不会进行展开
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value) // Vue 3 Guide
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value) // 0
ref
和reactive
功能上有些重复,可能是为了方便两种编程习惯
// ref
let x = 10
let y = 10
// reactive
const obj = {
x: 10,
y: 10
}
响应式状态解构(toRefs、toRef)
import { ref, reactive, toRefs, toRef } from 'vue'
export default {
setup() {
const oName = ref('wmm')
const oJob = ref('fe')
const oFav = ref('basketball')
const person = reactive({
name: oName,
age: 18
})
person.job = oJob
person.fav = oFav.value
// 直接解构得到的是解构出来的值,会丢失响应性
// const { name, age, job, fav } = person
// console.log(name, age, job, fav) // wmm 18 fe basketball
// 使用toRefs(proxy)批量解构
const { name, age, job, fav } = toRefs(person)
console.log(name, age, job, fav)
// 使用toRef(proxy, key)解构某个property
const age1 = toRef(person, 'age')
console.log(age1)
age1.value++
console.log(person.age, age.value, age1.value) // 19 19 19
}
}
readonly
基于一个对象(响应式或纯对象)或ref
,返回原始proxy
的只读proxy
对象
是深层的,访问的任何嵌套property
也是只读的
const person = reactive({
name: 'wmm',
age: 18
})
const copy = readonly(person)
console.log(copy) // proxy对象
person.age++
console.log(person.age, copy.age) // 19 19
copy.age++ // warn
isReactive
如果 proxy
是 readonly
创建的,但还包装了由 reactive
创建的另一个 proxy
,它也会返回 true
const state = reactive({
name: 'wmm'
})
// 从普通对象创建的只读 proxy
const plain = readonly({
name: 'wmm66'
})
console.log(isReactive(plain)) // false
// 从响应式 proxy 创建的只读 proxy
const stateCopy = readonly(state)
console.log(isReactive(stateCopy)) // true
发表评论