• 作者:老汪软件技巧
  • 发表时间:2024-12-19 11:07
  • 浏览量:

书接上文一篇文章,加深对vue响应式的理解

Vue3的响应式

大家或多或少也都听过这么一个构造方法Proxy,了解Vue3的响应式之前,我们有必要了解一下这个工具,这里推荐一下阮一峰老师的文章,。

简单的概括一下,Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。在与操作对象之间设置了一层间隔,我们对其操作,只需要与这层代理交互,就好比老板的秘书,它接受两个参数:target,handler,

const p = new Proxy(target, handler)

了解了Vue2响应式的基础实现,相信可以更好的去理解Proxy给我们带来的便利。

实现

到这里是不是有点像数据劫持的味道了,那么,不一样来了

image.png

我们可以看见get和set的用法,既然是代理对象属性的读取,那么我们是否可以直接return target[key]呢?所以这里不会无限递归访问对吧。

那么这里,我们还可以优化一下,如果你有看阮一峰老师的文章,在Proxy下面,不知道你是否好奇去看一了下,由于Object对象身上有了太多的方法,于是打造了一个新的API,作为替身,将来会把需要在Object身上新增的方法添加在Reflect身上,也就是说,Reflect和Object一样使用就行。另外,因为Object身上的某些方法并不优雅甚至报错会造成程序终止,对于程序员并不友好,比如我们之前用到的defineProperty在无法定义属性时会抛出一个错误,而我们使用Reflect则只会返回false。更多的,还需要我们多加地去阅读。

image.png

那么我们写到这里,如果是这样,会输出什么呢?

const data = {
    a: 1,
    b: {
        e: 2,
        f: 3
    },
    c: [1, 2, 3]
}
const proxy = reactive(data)
console.log(proxy.b.e);//get  2

看来,和数据劫持一样,依然需要递归,那么两者又有什么区别呢?

当然有,我们可以这样写:

image.png

那么区别来了,在数据劫持中,我们是默认递归,无论是否访问,都会递归地去劫持,而在这里,我们只是按需递归。同时,我尝试去验证数据劫持中遗留的问题,都解决了。我们不必再去重写数组的方法等等一些繁琐的操作。

那么问题又来了,既然这样,我们每次访问到同一个地方,都会去创建一个新的代理对象,或者,可能由于疏忽,会多次的代理同一个对象,很不优雅。

所以,尤大创建了两个数据结构来存储, new WeakMap()用来存放原对象:代理对象,new WeakSet() 用来存放代理过的对象所以,在我们进行代理之前,进行判断,如果WeakMap已存在该对象代理过的,就可以拿到第一次代理的对象,如果没有,则判断是否WeakSet中存在,如果存在,说明当前就是第一次代理过的对象,否则,进行代理。

const toProxy = new WeakMap()
const toRow = new WeakSet()
const isObject = (target) => {
    return typeof target == 'object' && target != null
}
const handler = {

响应式原理vue_响应式写法_

get(target, key, receiver) { console.log('get',target[key]); let reult = Reflect.get(target, key, receiver) return isObject(reult) ? reactive(reult) : reult }, set(target, key, value, receiver) { console.log('set',value); updateView() return Reflect.set(target, key, value, receiver) } } const updateView = () => { console.log('updateView'); } const createReactiveObject = (target) => { if (!isObject(target)) { return target } if(toProxy.has(target)){ return toProxy.get(target) } if(toRow.has(target)){ return target } let proxied = new Proxy(target, handler) toProxy.set(target,proxied) toRow.add(target) return proxied } const reactive = (target) => { return createReactiveObject(target) }

数据劫持中的遗留问题,一一解决了,并且代码更为的优雅。如果你读到这里,希望能够给自己几分钟整理一下整个响应式的构建过程,当然,本文只是简单的实现了数据的响应式,并不完善,还有很多边缘操作需要兼顾。

重新整理,本人也有收益,学无止境,温故而知新。