What is the Reactivity System?
Reactive objects are JavaScript proxies that behave like normal objects. The difference is that Vue can track property access and changes on reactive objects.
响应式对象是像普通对象一样行为的 JavaScript 代理。不同之处在于 Vue 可以跟踪响应式对象上的属性访问和变化。
One of Vue's most distinctive features is its modest Reactivity System. The state of a component is composed of reactive JavaScript objects. When the state changes, the view is updated.\ Vue 最显著的特点之一是其简洁的响应式系统。组件的状态由响应式 JavaScript 对象组成。当状态变化时,视图会更新。
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy是响应式的关键。
const o = new Proxy(
{ value: 1, value2: 2 },
{
get(target, key, receiver) {
console.log(`target:${target}, key: ${key}`)
return target[key]
},
set(target, key, value, receiver) {
console.log('hello from setter')
target[key] = value
return true
},
},
)
Reflect
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与
proxy handler 的方法相同。
Reflect
不是一个函数对象,因此它是不可构造的。
Reflect 可以完成对对象的基本操作
什么是对象的基本操作?
目前所有的操作(调用、语法)都是间接的使用对象的基本操作。间接操作可能会导致一些其他问题。
Object.keys()
拿不到无法枚举的值,那么就可以使用Reflect.ownKeys()
。Reflect
可以Reflect(target, key, otherTarget)
解决了什么问题?
const obj = {
a: 1,
b: 2,
get c() {
return this.a + this.b
}
}
// 如果直接 获取的话 那么 获取 target['c']时,get c 中的 this是 `obj`,而不是被 Proxy 代理的新对象。那么 get c 中 访问 this.a、this.b 时,不会触发 a、b 的 getter。 如果使用 Reflect 的话,直接 Reflect.get(target, key, receiver)即可。
import { track, trigger } from './effect'
import { reactive } from './reactive'
export const mutableHandlers: ProxyHandler<object> = {
get(target: object, key: string | symbol, receiver: object) {
track(target, key)
const res = Reflect.get(target, key, receiver)
// reactive 只支持对象
if (res !== null && typeof res === 'object') {
return reactive(res)
}
return res
},
set(target: object, key: string | symbol, value: unknown, receiver: object) {
let oldValue = (target as any)[key]
Reflect.set(target, key, value, receiver)
// 检查值是否已更改
if (hasChanged(value, oldValue)) {
trigger(target, key)
}
return true
},
}
const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)
Vue 的响应式系统核心概念
Vue.js 的响应式系统涉及 target
,Proxy
,Reflect
, ReactiveEffect
,Dep
,track
,trigger
,targetMap
和 activeEffect
(目前是 activeSub
)这几个概念
名称 | 说明 |
---|---|
target | 响应式目标对象 |
targetMap | 存储所有响应式目标及其依赖的全局 WeakMap |
ReactiveEffect | 包装更新逻辑的类(如 updateComponent) |
activeEffect | 当前正在执行的 ReactiveEffect |
Dep | 存储一个属性对应的所有 ReactiveEffect |
track | 依赖收集函数 |
trigger | 依赖触发函数 |
targetMap: WeakMap
└─ target1: Map (KeyToDepMap)
├─ key1: Dep(Set of ReactiveEffect)
└─ key2: Dep(...)
└─ target2: ...
type Target = any // 响应式目标
type TargetKey = any // 目标拥有的任何键
const targetMap = new WeakMap<Target, KeyToDepMap>() // 在此模块中定义为全局变量
type KeyToDepMap = Map<TargetKey, Dep> // 目标的键和效果的映射
type Dep = Set<ReactiveEffect> // dep 有多个 ReactiveEffects
class ReactiveEffect {
constructor(
// 这里,您给出想要实际应用为效果的函数(在这种情况下是 updateComponent)
public fn: () => T,
) {}
}
也就是说 targetMap
中,key
是 target
, 每一个 target
对应的是他们自己的依赖总Map
,即 KeyToDepMap
, KeyToDepMap
是target
中每个key
的依赖集合,集合中每一项都是ReactiveEffect
import { track, trigger } from './effect'
import { reactive } from './reactive'
export const mutableHandlers: ProxyHandler<object> = {
get(target: object, key: string | symbol, receiver: object) {
track(target, key)
const res = Reflect.get(target, key, receiver)
// reactive 只支持对象
if (res !== null && typeof res === 'object') {
return reactive(res)
}
return res
},
set(target: object, key: string | symbol, value: unknown, receiver: object) {
let oldValue = (target as any)[key]
Reflect.set(target, key, value, receiver)
// 检查值是否已更改
if (hasChanged(value, oldValue)) {
trigger(target, key)
}
return true
},
}
const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)