De pie sobre los hombros de gigantes y mirando vue3-Capítulo 15 Descripción general de la tecnología central del compilador

De pie sobre los hombros de gigantes y viendo Vue, el diseño y la implementación de Vue de Huo Chunyang. El autor interpreta la implementación de la capa inferior de vue3 paso a paso en forma de pregunta. La idea es muy ingeniosa. Aquí, la lógica principal del libro se conectará en serie, y también es un registro después de leerlo. Espero que a través de esta forma pueda comunicarme y aprender con todos.

Capítulo 5. Esquemas de respuesta para valores no primitivos

5.1 Comprender Proxy y Reflect

El proxy puede crear un objeto que puede representar otros objetos. Cabe señalar que el proxy solo puede representar objetos y no puede representar valores que no sean objetos, como cadenas y valores booleanos.

Reflect es un objeto global con los mismos métodos que los objetos, pero acepta un tercer receptor de parámetros. Puede entenderse así en el proceso de llamada de función.

const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return target[key]
  },
  set(target, key, newVal) {
    target[key] = newVal
    trigger(target, key)
  }
})
复制代码

El proxy anterior de Proxy obtenía el valor directamente a través del objeto, por lo que habrá un problema, es decir, este en get puede cambiar la colección de dependencia que no se puede responder, por lo que podemos reemplazar el método anterior a través de la característica de reflexión.

const obj = new Proxy(data, {
  get(target, key, receiver) {
    track(target, key)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newVal) {
    target[key] = newVal
    trigger(target, key)
  }
})
复制代码

5.2 Cómo funcionan los objetos y el proxy

La semántica real de un objeto está especificada por los métodos internos del objeto. Los métodos internos se refieren a los métodos que se llaman dentro del motor cuando operamos en un objeto. Un método interno [[Call]] se implementa dentro de un objeto de función, mientras que un objeto normal no lo hace.

La función de intercepción especificada cuando se crea el objeto proxy se usa realmente para personalizar los métodos y comportamientos internos del propio objeto proxy, no para especificar los métodos y comportamientos internos del objeto proxy.

5.3, cómo representar un objeto

Pregunta 1: Cómo interceptar la clave en obj

El resultado del operador in se obtiene llamando a un método abstracto llamado HasProperty. La función de intercepción correspondiente se llama has, por lo que necesitamos definir el método has al interceptar

const obj = new Proxy(data, {
  get(target, key, receiver) {
    track(target, key)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newVal) {
    target[key] = newVal
    trigger(target, key)
  },
  has(target, key) {
    track(target, key)
    return Reflect.has(target, key)
  }
})
复制代码

Problema 2: for... en bucle

De manera similar, el objeto de intercepción correspondiente a for...in es ownKeys, por lo que debemos definir el método ownKeys al interceptar. Y defina una Symbolfunción para almacenar la colección de efectos secundarios, cuando se ejecuta la función de activación, la función de efectos secundarios asociada con ITERATE_KEY debe extraerse y ejecutarse.

const ITERATE_KEY = Symbol()
ownKeys(target) {
  track(target, ITERATE_KEY)
  return Reflect.ownKeys(target)
}
// trigger 函数
const iterateEffects = depsMap.get(ITERATE_KEY)
iterateEffects && iterateEffects.forEach(effectFn => {
  if (effectFn !== activeEffect) {
    effectsToRun.add(effectFn)
  }
})
复制代码

Pregunta 3: eliminar propiedad eliminar obj.foo

delete对应的属性deleteProperty。因此我们在拦截的时候需要定义deleteProperty方法,并且删除的时候不需要执行副作用。

deleteProperty(target, key) {
  const hadKey = Object.hasOwnProperty.call(target, key)
  const res = Reflect.deleteProperty(target, key)
  if (res && hadKey) {
    trigger(target, key, 'DELETE')
  }
  return res
}
复制代码

5.4、合理低触发响应

问题4:当改变值的没变,不触发响应,,添加比较,处理一下NaN 的情况。

set(target, key, newVal, receiver) {
    const oldVal = target[key]
    const type = Object.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
    const res = Reflect.set(target, key, newVal, receiver)
    if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
      trigger(target, key, type)
    }
    return res
  },
复制代码

问题5:原型上继承的属性

const obj = {}
const proto = { bar: 1 }
const child = reactive(obj)
const parent = reactive(proto)
Object.setPrototypeOf(child, parent)
effect(() => {
    console.log(child.bar)
})
child.bar = 2

// 1
// 2
// 2
复制代码

副作用执行了两次。

当读取child.bar属性值时,由于child代理的对象obj自身没有bar属性,因此就会获取对象的obj的原型,得到parent.bar 的值。同理设置属性的时候也会寻找原型上的值,这时候,child、parent都建立了响应式,所以副作用执行了两次。

解决方法,只需要判断receiver是否是target的带来对象即可,只有当receiver是target的带来对象时才会触发更新,这样就能够屏蔽了由原型引起的更新。代理对象可以通过raw属性读取原始数据

child.raw === obj // true

parent.raw === proto // true

在set中判断receiver是不是target的代理对象

if (target === receiver.raw) {
  if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
    trigger(target, key, type)
  }
}
复制代码

5.5、浅响应与深响应

当读取属性值是,首先检测该值是否是对象,如果是对象,则递归的调用 reactive函数将其包装成响应式数据并返回,这样就首先了深度响应。通过设置一个isShallow变量,来控制。

function creatReactive(obj, isShallow = false) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      if (key === 'raw') {
        return target
      }
			const res = Reflect.get(target, key, receiver)
			if (isShallow) {
				return res
			}
      track(target, key)
      
      if (typeof res === 'object' && res !== null) {
        return reactive(res)
      }
      return res
    },
    set(target, key, newVal, receiver) {
      // 
    }
  })
}
复制代码

5.6、只读和浅只读

createReactive函数接收一个 isReadonly 的变量,在 set 的时候判断是否是只读。并且当第一个对象是只读属性时,在 get 中也不需要调用 track 函数追踪响应。

get() {
	if (!isReadonly) {
		track(target, key)
	}
},
set() {
  if (isReadonly) {
    console.warn(`属性 ${key} 是只读信息`)
    return true
  }
}
复制代码

5.7、代理数组

5.7.1、数组的索引与长度

Supongo que te gusta

Origin juejin.im/post/7085354700823855117
Recomendado
Clasificación