一文带你深入理解Vue3响应式原理

 更新时间:2022年11月10日 09:44:36   作者:小满zs  
响应式就是当对象本身(对象的增删值)或者对象属性(重新赋值)发生变化时,将会运行一些函数,最常见的就是render函数,下面这篇文章主要给大家介绍了关于Vue3响应式原理的相关资料,需要的朋友可以参考下

 响应式原理

Vue2 使用的是 Object.defineProperty  Vue3 使用的是 Proxy

2.0的不足

对象只能劫持 设置好的数据,新增的数据需要Vue.Set(xxx)  数组只能操作七种方法,修改某一项值无法劫持。

reactive和effect的实现

export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object
 
 
          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)
 
 
           return res
        }
    })
}

 Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作。

effect track trigger

实现effect 副作用函数

let activeEffect;
export const effect = (fn:Function) => {
     const _effect = function () {
        activeEffect = _effect;
        fn()
     }
     _effect()
}

 使用一个全局变量 active 收集当前副作用函数,并且初始化的时候调用一下

实现track

const targetMap = new WeakMap()
export const track = (target,key) =>{
   let depsMap = targetMap.get(target)
   if(!depsMap){
       depsMap = new Map()
       targetMap.set(target,depsMap)
   }
   let deps = depsMap.get(key)
   if(!deps){
      deps = new Set()
      depsMap.set(key,deps)
   }
 
   deps.add(activeEffect)
}

执行完成成后我们得到一个如下的数据结构 

实现trigger

export const trigger = (target,key) => {
   const depsMap = targetMap.get(target)
   const deps = depsMap.get(key)
   deps.forEach(effect=>effect())
}

 当我们进行赋值的时候会调用 set 然后 触发收集的副作用函数

import {track,trigger} from './effect'
 
export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object
 
          track(target,key)
 
          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)
 
           trigger(target,key)
 
           return res
        }
    })
}

 给 reactive 添加这两个方法

测试代码

<!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>Document</title>
</head>
 
<body>
 
    <div id="app">
 
    </div>
 
    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}`
        })
 
        setTimeout(()=>{
            user.name = '大满很厉害'
            setTimeout(()=>{
                user.age = '23'
            },1000)
        },2000)
 
    </script>
</body>
 
</html>

递归实现reactive

import { track, trigger } from './effect'
 
const isObject = (target) => target != null && typeof target == 'object'
 
export const reactive = <T extends object>(target: T) => {
    return new Proxy(target, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver) as object
 
            track(target, key)
 
            if (isObject(res)) {
                return reactive(res)
            }
 
            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
 
            trigger(target, key)
 
            return res
        }
    })
}
<!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>Document</title>
</head>
 
<body>
 
    <div id="app">
 
    </div>
 
    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18,
            foo:{
                bar:{
                    sss:123
                }
            }
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
        })
 
        setTimeout(()=>{
            user.name = '大满很厉害'
            setTimeout(()=>{
                user.age = '23'
                setTimeout(()=>{
                    user.foo.bar.sss = 66666666
                },1000)
            },1000)
        },2000)
 
    </script>
</body>
 
</html>

总结

到此这篇关于Vue3响应式原理的文章就介绍到这了,更多相关Vue3响应式原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue-cli3.0如何使用prerender-spa-plugin插件预渲染

    vue-cli3.0如何使用prerender-spa-plugin插件预渲染

    这篇文章主要介绍了vue-cli3.0如何使用prerender-spa-plugin插件预渲染,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 一文详解Vue响应式数据的原理

    一文详解Vue响应式数据的原理

    在vue2的响应式中,存在着添加属性、删除属性、以及通过下标修改数组,但页面不会自动更新的问题,而这些问题在vue3中都得以解决,本文就给大家详细的介绍一下Vue响应式数据原理,感兴趣的小伙伴跟着小编一起来看看吧
    2023-08-08
  • vue.js实现请求数据的方法示例

    vue.js实现请求数据的方法示例

    这篇文章主要给大家介绍了vue.js实现请求数据的方法示例,文中分别介绍了vue1.0和vue2.0的示例方法,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-02-02
  • vue中的router-view组件的使用教程

    vue中的router-view组件的使用教程

    这篇文章主要介绍了vue中的router-view组件的使用教程,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-10-10
  • Javascript对象及Proxy工作原理详解

    Javascript对象及Proxy工作原理详解

    这篇文章主要为大家介绍了Javascript对象及Proxy工作原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Vue组件通信之父传子与子传父深入探究

    Vue组件通信之父传子与子传父深入探究

    对于vue来说,组件之间的消息传递是非常重要的,用vue可以是要组件复用的,而组件实例的作用域是相互独立,这意味着不同组件之间的数据无法互相引用,一般来说,组件之间可以有几种关系,下面是我对组件之间消息传递的常用方式的总结
    2022-12-12
  • vue.js 动态组件详解

    vue.js 动态组件详解

    这篇文章主要介绍了vue.js 动态组件详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • vue组件生命周期钩子使用示例详解

    vue组件生命周期钩子使用示例详解

    这篇文章主要为大家介绍了vue组件生命周期钩子使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • vue中使用词云图的实现示例

    vue中使用词云图的实现示例

    本文主要介绍了vue中使用词云图的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • SpringBoot实现全局和局部跨域的两种方式

    SpringBoot实现全局和局部跨域的两种方式

    本文主要介绍了SpringBoot实现全局和局部跨域的两种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01

最新评论