本文已参与「新人创作礼」活动,一起开启掘金创作之路。
了解Vue的数据劫持
-
Vue采用什么进行数据劫持?
- vue2 :
Object.defineProperty - vue3 :
Proxy
- vue2 :
-
为什么要取代?
Object.defineProperty监听不到数组变化(除vue重写的数组的8个方法)
this.arr[0] = 'Hello world' //监听不到
Vue.$set() // 使用$set()可以解决
-
Vue3采用Proxy劫持数据
- 数据劫持
- 观察者 Observer
- 数据解析 Compare
Proxy是什么?
MDN:Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
通俗理解的话,就是在目标对象之前设置一层“拦截”,当外界访问该对象的时候,都必须先通过这层拦截,因此可以对外界的访问进行过滤和改写。
Proxy代理的是谁?
Proxy代理的是数据,这里的数据可以是对象 / 数组 / 方法 (Object.defineProperty只能代理对象)。
实现简单的双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>实现简单的双向绑定</title>
</head>
<body>
<div>
<input type="text" id="input">
<p id="show"></p>
</div>
<script>
let obj = {}
const input = document.getElementById('input')
// 设置代理
let newObj = new Proxy(obj,{
set(target,key,value){
// vue3里面此段代码采用 发布订阅的模式 传递到订阅者
// 调用diff算法 对比改变 更新
if (key === 'text') {
input.value = value;
show.innerHTML = value
}
// this.subtribe.emit('',value)
return Reflect.set(target,key,value)
},
get(target,key){
return Reflect.get(target,key)
}
})
input.addEventListener('keyup',function(e){
newObj.text = e.target.value
})
</script>
</body>
</html>
Proxy有13种拦截方法
interface ProxyHandler<T extends object> {
/**
* A trap method for a function call.
* @param target The original callable object which is being proxied.
*/
apply?(target: T, thisArg: any, argArray: any[]): any;
/**
* A trap for the `new` operator.
* @param target The original object which is being proxied.
* @param newTarget The constructor that was originally called.
*/
construct?(target: T, argArray: any[], newTarget: Function): object;
/**
* A trap for `Object.defineProperty()`.
* @param target The original object which is being proxied.
* @returns A `Boolean` indicating whether or not the property has been defined.
*/
defineProperty?(target: T, property: string | symbol, attributes: PropertyDescriptor): boolean;
/**
* A trap for the `delete` operator.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to delete.
* @returns A `Boolean` indicating whether or not the property was deleted.
*/
deleteProperty?(target: T, p: string | symbol): boolean;
/**
* A trap for getting a property value.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to get.
* @param receiver The proxy or an object that inherits from the proxy.
*/
get?(target: T, p: string | symbol, receiver: any): any;
/**
* A trap for `Object.getOwnPropertyDescriptor()`.
* @param target The original object which is being proxied.
* @param p The name of the property whose description should be retrieved.
*/
getOwnPropertyDescriptor?(target: T, p: string | symbol): PropertyDescriptor | undefined;
/**
* A trap for the `[[GetPrototypeOf]]` internal method.
* @param target The original object which is being proxied.
*/
getPrototypeOf?(target: T): object | null;
/**
* A trap for the `in` operator.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to check for existence.
*/
has?(target: T, p: string | symbol): boolean;
/**
* A trap for `Object.isExtensible()`.
* @param target The original object which is being proxied.
*/
isExtensible?(target: T): boolean;
/**
* A trap for `Reflect.ownKeys()`.
* @param target The original object which is being proxied.
*/
ownKeys?(target: T): ArrayLike<string | symbol>;
/**
* A trap for `Object.preventExtensions()`.
* @param target The original object which is being proxied.
*/
preventExtensions?(target: T): boolean;
/**
* A trap for setting a property value.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to set.
* @param receiver The object to which the assignment was originally directed.
* @returns `A `Boolean` indicating whether or not the property was set.
*/
set?(target: T, p: string | symbol, newValue: any, receiver: any): boolean;
/**
* A trap for `Object.setPrototypeOf()`.
* @param target The original object which is being proxied.
* @param newPrototype The object's new prototype or `null`.
*/
setPrototypeOf?(target: T, v: object | null): boolean;
}
针对数据的拦截
- get
- set
- has
- deleteProperty
遍历
- ownKeys
属性描述器
- getOwnPropertyDescriptor
- defineProperty 设置修饰器
是否可以拓展
- preventExtensions:阻止扩展
- isExtensibe:是否可扩展