• 读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>Documenttitle>
    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    总结

    • 使用 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>Documenttitle>
      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>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
    • 通过定义一个临时变量作为中转, 可以让 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>Documenttitle>
    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    总结

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

    4. 响应式原理 - 简介

    在这里插入图片描述

    讲解

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

    在这里插入图片描述

    总结

    1. 初始化 data 的时候, 调用 observe() 方法给 data 里的属性定义 get 方法和 set 方法
    2. 渲染真实 DOM 的时候, Watcher 会访问页面上使用的属性变量
    3. 由于 Object.defineProperty() 的特点, 会自动执行 get 方法, 给数据的 Dep 都加上渲染函数,每次修改数据时通知渲染 Watcher 更新视图
  • 相关阅读:
    SELinux
    【LLM】基于LLM的agent应用(上)
    Vue---单向数据
    day27-完全平方数(背包问题)
    etcdctl 恢复k8s后报错
    vector的介绍以及使用方式
    一个 Angular 开发人员对腾讯 Cloud Studio 使用后的体验报告
    CLion 2023:专注于C和C++编程的智能IDE mac/win版
    计算机毕业设计java+ssm阳光心理健康网站的设计与实现
    Win32简单图形界面程序逆向
  • 原文地址:https://blog.csdn.net/m0_62371650/article/details/128180508