🌵理解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原理的其他文章~

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

相关文章
|
1月前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
49 9
|
9天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
40 0
|
2月前
|
API
vue3知识点:响应式数据的判断
vue3知识点:响应式数据的判断
27 3
|
2月前
|
JavaScript 前端开发 API
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
25 0
|
12天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
28 1
vue学习第四章
|
12天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
25 1
vue学习第九章(v-model)
|
12天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
27 1
vue学习第十章(组件开发)
|
18天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
18天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
18天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。