Proxy对象和Vue3

Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

mdn-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 {
    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, mutableHandlersreadonlyHandlersshallowReactiveHandlersshallowReadonlyHandlers。 这里先介绍 mutableHandlers 劫持代理做多的, 因为其他三种 handler 也算是 mutableHandlers 的变形版本。

// vue-next/packages/reactivity/src/baseHandlers.ts
export const mutableHandlers: ProxyHandler = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

 
 
function createReactiveEffect(
  fn: (...args: any[]) => T,
  options: ReactiveEffectOptions
): ReactiveEffect {
  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
  1. trackStack trackStack.push(shouldTrack); shouldTrack = true; traskStack push一个标记
  2. effectStack.push(effect) push effect函数
  3. 当前激活的activeEffect更改为 effect
  4. return fn(...args) 返回一个函数

你可能感兴趣的:(Proxy对象和Vue3)