美文网首页
Proxy对象和Vue3

Proxy对象和Vue3

作者: 晗笑书生 | 来源:发表于2020-06-22 11:28 被阅读0次

    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<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, mutableHandlersreadonlyHandlersshallowReactiveHandlersshallowReadonlyHandlers。 这里先介绍 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
    1. trackStack trackStack.push(shouldTrack); shouldTrack = true; traskStack push一个标记
    2. effectStack.push(effect) push effect函数
    3. 当前激活的activeEffect更改为 effect
    4. return fn(...args) 返回一个函数

    相关文章

      网友评论

          本文标题:Proxy对象和Vue3

          本文链接:https://www.haomeiwen.com/subject/wtwexktx.html