读vue源码搞懂响应式原理

vue2响应式原理

  • Vue2是借助Object.defineProperty()实现的,而Vue3是借助Proxy实现的
  • 想深入学习 Vue2 的响应式原理, 需要先学习 Object.defineProperty() 方法
  • 为了称呼方便, 后续说 Vue 响应式原理统一指 Vue2 的响应式原理

1.Object.defineProperty 方法 - 简介

  • 定义一个对象 obj, 需要具备 name / age 等属性
  • age变化时, 在控制台打印最新的值
  • 当访问 obj.age 时, 控制台会显示 xxx 读取了 age 属性

演示

<!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="box">
    <p></p>
    <button id="btn">修改年龄</button>
  </div>
  <script>
    const obj = {
      name: 'zs'
    }

    Object.defineProperty(obj, 'age', {
      // 访问 obj.age 时这个方法会自动执行
      get() {
        console.log('有人访问了 age !', temp)
        return 18
      },
      // 给 obj.age 赋值时这个方法会自动执行, 我们也称之为拦截
      set(newVal) {
        console.log(newVal)
      } 
    })
    
    document.querySelector('p').innerHTML = `${ obj.name } --- ${ obj.age }`

    document.querySelector('#btn').onclick = function() {
      obj.age = 20
    }

  </script>
</body>
</html>

总结

  • 使用 Object.defineProperty() 可以无痕的监视到对象的属性什么时候发生变化, 什么时候被访问

2.Object.defineProperty 方法 - 利用临时变量来中转

  • 使用了 Object.defineProperty() 后发现 get / set 并不好用
  • 无法设置数据, 同时也不能获取到变化的属性

解决方法

  • 定义一个临时变量来中转

    <!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="box">
        <p></p>
        <button id="btn">修改年龄</button>
      </div>
      <script>
        const obj = {
          name: 'zs'
        }
    
        let temp = 18
    
        Object.defineProperty(obj, 'age', {
          // 访问 obj.age 时这个方法会自动执行
          get() {
            // 一般会在这里收集依赖, 例如: 网页中 p / h1 / div 等标签要渲染 age 数据
            // 注意: 这里是收集这些依赖, 下面会在 set 中自动通知这些依赖来更新 DOM
            console.log('有人访问了 age !', temp)
            return temp
          },
          // 给 obj.age 赋值时这个方法会自动执行, 我们也称之为拦截
          set(newVal) {
            // 一般当数据变更时, 通知依赖项更新 DOM
            console.log(newVal)
            temp = newVal
          } 
        })
        
        document.querySelector('p').innerHTML = `${ obj.name } --- ${ obj.age }`
    
        document.querySelector('#btn').onclick = function() {
          obj.age ++
        }
    
      </script>
    </body>
    </html>
    
  • 通过定义一个临时变量作为中转, 可以让 get / set 方法用起来就像普通属性去获取 / 修改

3.Object.defineProperty 方法 - 使用闭包进行封装 defineReactive 函数

  • 利用闭包, 来封装一个方法
  • val 局部变量被内层函数 get/ set 使用, 形成了闭包
<!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="box">
    <p></p>
    <button id="btn">修改年龄</button>
  </div>
  <script>
    const obj = {
      name: 'zs'
    }

    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        // 访问 obj.age 时这个方法会自动执行
        get() {
          // 一般会在这里收集依赖, 例如: 网页中 p / h1 / div 等标签要渲染 age 数据
          // 注意: 这里是收集这些依赖, 下面会在 set 中自动通知这些依赖来更新 DOM
          console.log(`有人访问了 ${ key } !`, val)
          return val
        },
        // 给 obj.age 赋值时这个方法会自动执行, 我们也称之为拦截
        set(newVal) {
          // 一般当数据变更时, 通知依赖项更新 DOM
          console.log(newVal)
          val = newVal
        } 
      })
    }

    defineReactive(obj, 'age', 18)
    
    document.querySelector('p').innerHTML = `${ obj.name } --- ${ obj.age }`

    document.querySelector('#btn').onclick = function() {
      obj.age ++
    }

  </script>
</body>
</html>

总结

  • 通过利用闭包的特性, 封装一个函数, 可以很好的定义对象的「响应式属性」
  • 将来 Vue 就是利用这一特性, 来实现的响应式原理

4. 响应式原理 - 简介

在这里插入图片描述

讲解

  • Vue 响应式核心就是利用了两个类, Dep 类和 Watcher
  • 每个定义在 data 中的数组、对象中都会有一个 dep 属性,访问属性的时候 get 方法会收集对应的 Watcher

在这里插入图片描述

总结

  1. 初始化 data 的时候, 调用 observe() 方法给 data 里的属性定义 get 方法和 set 方法
  2. 渲染真实 DOM 的时候, Watcher 会访问页面上使用的属性变量
  3. 由于 Object.defineProperty() 的特点, 会自动执行 get 方法, 给数据的 Dep 都加上渲染函数,每次修改数据时通知渲染 Watcher 更新视图
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

史蒂文·月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值