🌵理解Vue3响应式原理

简介: 🌵理解Vue3响应式原理

前言


关于Vue的响应式实现是Vue原理的重要组成部分,正确理解Vue的响应式更有利于我们理解和使用Vue。

这篇文章就来谈一谈Vue3是如何实现响应式的,此前Vue2实现响应式主要依靠的API是ES5的Object.defineProperty,我们来用它使对象变成可观察的,再结合依赖收集实现了响应式系统。 具体细节可参考下面的视频:


尤大大教你写Vue


一、Object.defineProperty的缺点


  • 深度监听需要一次性递归(较影响性能)
  • 无法监听新增属性/删除属性(可以通过Vue.set,Vue.delete实现)
  • 无法原生监听数组,需要特殊处理


于是Vue3为了解决这些问题,使用ES6的Proxy以及Reflect实现了响应式。Vue3使用ES6语法,这是现代JavaScript的东西,不支持IE浏览器,所以尽早放弃IE吧,赶快去下载Chrome!!!


二、Vue3响应式实现


2.1 理解ES6 Reflect


假设我们现在有一个商品,它拥有pricequantity属性。


let product = { price: 5, quantity: 2 }
复制代码


现在我们有三种方法打印出对象的属性:


  1. 首先,我们可以使用典型的“点”表示法


console.log('quantity is ' + product.quantity)
复制代码


  1. 我们也可以用括号表示法


console.log('quantity is ' + product['quantity'])
复制代码


  1. 最后我们还可以使用ES6 Reflect,就像你看到的。


console.log('quantity is '+ Reflect.get(product, 'quantity')).
复制代码


正如你所看到的,三个都有效,但是Reflect有一种超能力🤷‍♀️,在我们理解完Proxy之后,我会展示给你看。


2.2 理解ES6 Proxy


Proxy是另一个对象的占位符,默认情况下对该对象进行委托。


如果我创建一个proxiedProduct,然后声明一个Proxy,然后我调用proxiedProduct.quantity,他就会:


  1. 它会先调用Proxy
  2. 这个Proxy再调用产品
  3. 然后返回到Proxy
  4. 返回这个product到控制台日志
  5. 最后就会打印出2


let product = { price: 5, quantity: 2 }
let proxiedProduct = new Proxy(product, {})
console.log(proxiedProduct.quantity)
复制代码


简单来说就是一个对象委托


Proxy中的第二个参数,它叫做处理函数,在这个处理函数中,你可以传递一个诱捕器,它的作用就是可以让我们拦截基本操作,如属性查找,或枚举函数调用


接下来我们尝试着实现拦截get操作。


2.3 使用Proxy尝试拦截get操作


let product = { price: 5, quantity: 2 }
let proxiedProduct = new Proxy(product, {
    get(){
        console.log('Get was called')
        return 'Not the value'
    }
})
复制代码


在这个例子中,当我们调用proxiedProduct中的get时,我们想要改变get的行为。

现在当它被调用时,我们简单的做一下输出,我们来看看运行结果:


1febd3fe95d84e50bd7333756d363339_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


好啦,我们现在已经实现了,当获取属性值的时候,实现了自己的get方法,但是现在做的get方法完全无用,所以我们还是如实的返回值吧~


2.4 使用Proxy拦截get操作的正确姿势


let product = { price: 5, quantity: 2 }
let proxiedProduct = new Proxy(product, {
    get(target, key){
        console.log('Get was called with key= '+ key)
        return target[key]
    }
})
console.log(proxiedProduct.quantity)
复制代码


我们声明get的两个参数,然后我们输出'Get was called with key= key',我们将使用括号的表示法返回key属性的值,所以当我们调用conosle.log,它会通过get(target)调用我们的proxiedProduct,在这种情况下,这个参数target就是我们传递的product,我们的键是quantity,因为我们想得到quantity,然后就会正确输出,我们来看看结果:


74f1034b1eca4d74a3d39608c84242f6_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


之前我们提到过Reflect的作用很大,现在我们尝试在Proxy里使用Reflect


2.5 在Proxy里使用Reflect


let product = { price: 5, quantity: 2 }
let proxiedProduct = new Proxy(product, {
    get(target, key, receiver){
        console.log('Get was called with key= '+ key)
        // return target[key]
        return Reflect.get(target,key,receiver)
    }
})
console.log(proxiedProduct.quantity)
复制代码


我们现在会有一个附加参数,称为receiver,它将传递到我们的Reflect调用中。


它保证了当我们的对象有继承自其他对象的值或函数时,this指针能正确的指向使用的对象,这将避免一些我们在Vue2中有的响应式警告,打印结果当然还是不变的:


03c69e85929f4fad86c9d9e50ce5e277_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


我们还需要使用Proxy来拦截set方法,我们接下里就来实现它~


2.6 使用Proxy拦截set操作


let product = { price: 5, quantity: 2 }
let proxiedProduct = new Proxy(product, {
    get(target, key, receiver) {
        console.log('Get was called with key= '+ key)
        return Reflect.get(target,key,receiver)
    },
    set(target, key, value, receiver) {
        console.log('Set was called with key = ' + key + 'and value = ' + value)
        return Reflect.set(target, key, value, receiver)
    }
})
proxiedProduct.quantity = 100
console.log(proxiedProduct.quantity)
复制代码


我们的set方法接收target,key,value,receiver四个参数,我们将在set被调用时,打印出我们的keyvalue,那我们在调用Reflect.set,传递的参数是target,key,value,receiver,我们更改一下quantity的数量来测试看看:


08b885a4533646bb932098bbd88c367f_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


这样我们就成功的完成了set的拦截,还记得我说过Proxy的第二个参数称为处理函数吗?我们现在可以封装这部分处理函数,让它长的更像Vue3的源代码


2.7 封装处理函数,使其更像Vue3源码


function reactive(target){
    const handler = {
        get(target, key, receiver) {
            console.log('Get was called with key= '+ key)
            return Reflect.get(target,key,receiver)
        },
        set(target, key, value, receiver) {
            console.log('Set was called with key = ' + key + 'and value = ' + value)
            return Reflect.set(target, key, value, receiver)
        }
    }
    return new Proxy(target, handler)
}
let product = reactive({ price: 5, quantity: 2 })
product.quantity = 4
console.log(product.quantity)
复制代码


我们创建一个叫做reactive的函数,相信你如果使用过Composition API,你会看起来很熟悉,我们封装我们的get和set方法到常量handler中,最后我们创建一个新的Proxy,传递我们的target以及handler,现在我们声明一个product时,我们只需要传递一个对象到reactive函数中,reactive函数将返回一个Proxy,这个Proxy我们会把它当作原始对象来使用,我们更改quantity的值,然后控制台打印quantity的值,当我们执行这段代码,就得到了我们所期望的结果:


ed3c5709eac642579cfbee8804cac5eb_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


最后


⚽读到到这,相信大家对Vue3的响应式实现已经有了更深的认识,虽然Vue3现在仍存在一些浏览器兼容性问题,但是我认为不久后,IE会被淘汰,Vue3将取代Vue2,因此我们要更加积极的学习Vue3使用及原理,后期我还会更新关于Vue3原理的其他文章~

⚾如果你对这篇文章感兴趣欢迎点赞关注+收藏,更多精彩知识正在等你!😘

相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
143 64
|
2月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
115 60
|
10天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
39 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
39 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
33 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
42 1
|
2月前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
|
2月前
|
JavaScript 前端开发 开发者
Vue是如何劫持响应式对象的
Vue是如何劫持响应式对象的
33 1
|
2月前
|
JavaScript 前端开发 API
从Vue 2到Vue 3的演进
从Vue 2到Vue 3的演进
44 0
|
2月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
65 0

热门文章

最新文章