前言:vue2 响应式原理 Object.defineProperty,vue3 响应式原理 Proxy 代理。本文主要讲这两个 api 的本质区别。
1. Proxy
Proxy 能够拦截和重新定义对象的基本操作,那什么叫对象的基本操作呢,对象内部运行的方法就是对象的基本操作。对象的内部操作有 11 种:[[GetPrototypeOf]]、[[SetPrototypeOf]]、[[IsExtensible]]、[[PreventExtensions]]、[[GetOwnProperty]]、[[DefineOwnProperty]]、[[HasProperty]]、[[GET]]、[[SET]]、[[DELETE]]、[[OwnPropertyKeys]]。如果是函数对象的话,会多两个基本操作:[[Construct]]、[[Call]]
如下对象操作实际上就是内部运行函数:
let obj = { name: "yqcoder" }; Object.defineProperty(); // 实际上就是运行内部函数 [[DefineOwnProperty]] obj.name; // 实际上就是运行内部函数 [[GET]] obj.name = "YQcoder"; // 实际上就是运行内部函数 [[SET]] delete obj.name; // 实际上就是运行内部函数 [[DELETE]] // 实际上就是运行内部函数 [[OwnPropertyKeys]] for (let key in obj) { }
基本操作对应 property 的陷阱函数,什么叫陷阱函数,就是通过代理去读属性的时候,他本来应该是运行内部的[[GET]]基本操作的,但是最后走到了通过 Proxy 重新定义的 get() 函数了,没有运行内部的 [[GET]] 基本操作, 这就叫陷阱函数。
如下内部操作函数对应的陷阱函数:
[[GetOwnProperty]] => getPrototypeOf(); [[SetPrototypeOf]] => setPrototypeOf(); [[IsExtensible]] => isExtensible(); [[PreventExtensions]] => preventExtensions(); [[GetOwnProperty]] => getOwnPropertyDescriptor(); [[DefineOwnProperty]] => defineProperty(); [[HasProperty]] => has(); [[GET]] => get(); [[SET]] => set(); [[DELETE]] => deleteProperty(); [[OwnPropertyKeys]] => ownKeys()
2. DefineProperty
DefineProperty 本身就是个基本操作,他并不拦截基本操作,只是调用基本操作。所以在 vue2 中怎么处理数组的,因为拦截不到数组的 push、splice 等,直接重写了这些方法,改了原型,当这些数组去调方法的时候,实际是调用 vue2 重写的方法。
// vue2 Object.defineProperty(arr, "length", { set(value) {}, get() {}, }); // vue3 const p = new Proxy(arr, { get(target, prop) {}, set(target, prop, value) {}, });