vue3响应式原理(小满zs vue3 笔记八)

tip: 带着问题去理解响应式原理why,what,how

一. 响应式原理核心点是什么? 

** 数据截持 **
** 依赖收集 **
** 派发更新 **

二.vue2的响应式原理? vue3响应式原理?区别是什么?

 1. 下面vue2是官网上的这图片

 

** 通过 Object.defineProperty 遍历对象的每一个属性,把每一个属性变成一个 getter 和 setter 函数,读取属性的时候调用 getter,
给属性赋值的时候就会调用 setter.** 
** 当运行 render 函数的时候,发现用到了响应式数据,这时候就会运行 getter 函数,然后 watcher(发布订阅)就会记录下来。
当响应式数据发生变化的时候,就会调用 setter 函数,watcher 就会再记录下来这次的变化,然后通知 render 函数,
数据发生了变化,然后就会重新运行 render 函数,重新生成虚拟 dom 树。 ** 

下面是具体的调度图

 render -> watcher ($get 收集依赖, $set方法 派发更新), 执行Scheduler 调度队列里数据

vue2响应式原理总结:
1. 数据截持, vue2是通过Object.defineProperty来将对象的每一个属性转化成get,set;
其中修改对象的属性时就会触发set,使用对象的属性时就会触发get;
2. 依赖收集, 就是在渲染视图时,将watcher和具体的属性,通过发布订阅者模式管理,这样数据改变之后就能更精准的更新视图;
3. 派发更新, 它就是通过dep来执行watcher的notify方法.
** 使用Object.defineProperty做响应式的缺点 **
    1. 深度监听,需要一次性递归到底,计算量比较大;
    2. 描述符只有get和set,无法监听新增属性和删除属性的操作;
    3. 无法原生监听数组
这三个缺点中,第二点是defineProperty本身API的缺陷,而第一点和第三点都是出于性能考虑而做的取舍;
** 解决缺点的方法 **
当我们通过数组的方法去更改数组时或是直接删除data数据,数据并不能实现响应式,因为Object.defineProperty是没有办法处理属性删除和新增的
因此vue2的响应式,通过数组方法(prop,push),或是删除,vue是不能监听的
vue2中通过vue2中可以通过vue.Delete和vue.Set这些vue内置api来改变属性,实现响应式。

2.0的不足

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

2. vue3 响应式原理(需要通过tsc把.ts变成js,目前index.html无内容待更新)

index.html

<!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 './effet.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.ts

import { track, trigger } from './effet'
// 递归实现reactive
const isObject = (target) => target != null && typeof target == 'object'
// Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作。
export const reactive = <T extends object>(target: T) => {
    // 第一个参数target
    return new Proxy(target, {
          // 第二个参数拦截器, target当前对象,key对象属性,reactive也是当前对象
        get(target, key, receiver) {
                // Reflect保证上下文正确取值
            const res = Reflect.get(target, key, receiver) as object
       // 收集依赖
            track(target, key)
 
            if (isObject(res)) {
                return reactive(res)
            }
 
            return res
        },
        // Reflect.set返回的是布尔值
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
 
            trigger(target, key)
 
            return res
        }
    })
}

effet.ts

// effect track trigger, 实现effect 副作用函数
// 使用一个全局变量 active 收集当前副作用函数,并且初始化的时候调用一下
let activeEffect: any;
export const effect = (fn:Function) => {
     const _effect = function () {
        activeEffect = _effect;
        fn()
     }
     _effect()
}
// 实现track,收集依赖
const targetMap = new WeakMap()
export const track = (target: any,key: any) =>{
   let depsMap = targetMap.get(target)
   if(!depsMap){// 第一次无值,对应value是一个Map
       depsMap = new Map()
       targetMap.set(target,depsMap)
   }
  // 对象取value
   let deps = depsMap.get(key)
   if(!deps){
      deps = new Set()
      depsMap.set(key,deps)
   }
   // 收集副作用依赖
   deps.add(activeEffect)
}
// 触发依赖
export const trigger = (target: any,key: any) => {
  const depsMap = targetMap.get(target)
  const deps = depsMap.get(key)
  deps.forEach(effect=>effect())
}

参考文章 :

vue2

https://blog.csdn.net/m0_54581080/article/details/126036155

https://blog.csdn.net/weixin_71718397/article/details/125859350

vue3 是blibli小满视频vue3及csdn

 

posted @ 2023-07-13 16:44  TheYouth  阅读(96)  评论(0编辑  收藏  举报