Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
// const p = new Proxy(target,handler)
// target可以是任意的Object Array Proxy等
var handler = {
// 属性读取操作的捕捉器
// 以下是传递给get方法的参数,this上下文绑定在handler对象上.入参
// target-目标对象。
// property-被获取的属性名。
// receiver-Proxy或者继承Proxy的对象
// 出参可以返回任意值
// handler.get 方法用于拦截对象的读取属性操作。
// 该方法会拦截目标对象的以下操作:
// 访问属性: proxy[foo]和 proxy.bar
// 访问原型链上的属性: Object.create(proxy)[foo]
// Reflect.get()
get: function(target, property, receiver) {
console.log('invoke get', target, property, receiver)
return target[property]
// return 1
},
// 属性设置操作的捕捉器。
// 以下是传递给 set() 方法的参数。this 绑定在 handler 对象上。
// target-目标对象。
// property-将被设置的属性名或 Symbol。
// value-新属性值。
// receiver-最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。
// 出参
// set() 方法应当返回一个布尔值。
// 返回 true 代表属性设置成功。
// 在严格模式下,如果 set() 方法返回 false,那么会抛出一个 TypeError 异常。
// 劫持操作
// 指定属性值:proxy[foo] = bar 和 proxy.foo = bar
// 指定继承者的属性值:Object.create(proxy)[foo] = bar
// Reflect.set()
set: function(target, prop, value, receiver) {
target[prop] = value;
console.log('invoke set property set: ' + prop + ' = ' + value, {target, prop, value, receiver});
return true;
},
// in 操作符的捕捉器。
has: function(target, prop) {
// todo somecheck or logic
console.log('invoke has', {target, prop});
if (prop[0] === '_') {
return false;
}
return prop in target;
},
// delete prop
deleteProperty: function(target, prop) {
console.log('invoke delete ', { target, prop})
return true
},
// 定义属性
defineProperty: function(target, prop, descriptor) {
console.log('invoke defineProperty ', { target, prop, descriptor})
return true;
},
// 方法用于拦截 Reflect.ownKeys()
ownKeys(target) {
console.log('invoke ownKeys', { target})
return Reflect.ownKeys(target);
}
}
var obj = {a:1, _a: 2, [Symbol('t')]: 12}
var p = new Proxy(obj, handler)
p.a = 3
console.log({c: p.a})
console.log('a' in p, Reflect.has(p, '_a') , Reflect.has(obj, '_a')) // 触发has的劫持 第三个原本的对象不触发
delete p['_a'] // delete p._a Reflect.delete(p, '_a') //触发delete
p.b = 'b' // 这个会触发 set的劫持
Object.defineProperty(p, 'd', {
get() {
console.log('------getter invoke')
return 4
},
// set(newVal) {
// console.log('------getter invoke')
// p.d = newVal
// },
// writable: true,
// enumerable: true,
// configurable: true
}) // 这个
p.d
for (let key of Object.keys(p)) {
console.log(key);
}
// --------
// var arr = [{a:1, b:2}, {a:3, b:4}]
// var pArr = new Proxy(arr, handler) // target可以是任何对象 数组
// console.log(pArr[0])
// pArr.push({a:3, b:5}) // 这个也会触发pArr的set劫持
// pArr.unshift({a:1, b:1})
// var pp = Object.create(p)
// pp.a = 4
// console.log({c: p.a, d:pp.a})
proxy能劫持的行为有以下14种
// 从定义中可以看出 共有以下14种代理 官方翻译为 包含捕捉器(trap)的占位符对象,可译为处理器对象。
interface ProxyHandler<T extends object> {
getPrototypeOf? (target: T): object | null;
setPrototypeOf? (target: T, v: any): boolean;
isExtensible? (target: T): boolean;
preventExtensions? (target: T): boolean;
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor | undefined;
has? (target: T, p: PropertyKey): boolean;
get? (target: T, p: PropertyKey, receiver: any): any;
set? (target: T, p: PropertyKey, value: any, receiver: any): boolean;
deleteProperty? (target: T, p: PropertyKey): boolean;
defineProperty? (target: T, p: PropertyKey, attributes: PropertyDescriptor): boolean;
enumerate? (target: T): PropertyKey[];
ownKeys? (target: T): PropertyKey[];
apply? (target: T, thisArg: any, argArray?: any): any;
construct? (target: T, argArray: any, newTarget?: any): object;
}
vue3 中 用到了以下5中来实现reactive get
set
deleteProperty
has
ownKeys
vue3
中使用Proxy
重写了响应式系统, 响应式系统又是 vue3
中的核心,而响应式的就是通过effect
track
来实现的。
baseHandlers 中主要包含四种 handler, mutableHandlers
、readonlyHandlers
、shallowReactiveHandlers
、 shallowReadonlyHandlers
。 这里先介绍 mutableHandlers
劫持代理做多的, 因为其他三种 handler 也算是 mutableHandlers
的变形版本。
// vue-next/packages/reactivity/src/baseHandlers.ts
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
function createReactiveEffect<T = any>(
fn: (...args: any[]) => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(...args: unknown[]): unknown {
if (!effect.active) {
return options.scheduler ? undefined : fn(...args)
}
if (!effectStack.includes(effect)) {
cleanup(effect)
try {
enableTracking()
effectStack.push(effect)
activeEffect = effect
return fn(...args)
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
effect.id = uid++
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
return
}
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key
})
}
}
}
调用栈如下
image.png- trackStack trackStack.push(shouldTrack); shouldTrack = true; traskStack push一个标记
- effectStack.push(effect) push effect函数
- 当前激活的activeEffect更改为 effect
- return fn(...args) 返回一个函数
网友评论