亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • 基础

  • 手写代码

    • 手写类型转换
    • 手写累加、累乘函数
    • 手写new
    • 手写深拷贝
    • 手写Object.create
    • 手写继承
    • 手写extends
    • 手写instanceof
    • 手写call、apply、bind
    • 手写jsonp
    • 手写getQueryString
    • 手写setInterval
    • 手写防抖与节流
    • 手写对象属性值迭代器
    • 手写分时函数
    • 手写事件委托
    • 手写图片懒加载
    • 手写原生Ajax请求
    • 手写AOP装饰函数
    • 手写柯里函数
    • 手写数组扁平化flat
    • 手写数组去重
    • 手写eventEmit类
    • 手写Vue数据响应式
      • 手写Vue nextTick
      • 手写Promise
    • JS底层深入

    • CSS

    • 基础
    • 手写代码
    0zcl
    2021-06-18
    目录

    手写Vue数据响应式

    注意

    简单实现Vue数据响应式原理

    答:

      function observer(data) {
        // 递归终止条件
        if (typeof data !== 'object') return
        if (data instanceof Array) {
          // 数组处理
        } else {
          for (let key in data) {
            defineReactive(data, key, data[key])
          }
        }
      }
    
      // 数据响尖 or 劫持
      function defineReactive(obj, key, value) {
        const dep = new Dep() // dep对象
        Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get: function() {
            // 依赖收集
            if (Dep.target) {
              dep.depend()
            }
            return value
          },
          set: function(newValue) {
            if (newValue === value) return
            value = newValue // 注意:更新值
            // 派发更新
            dep.notify()
          }
        })
      }
    
      class Dep {
        constructor() {
          this.subs = []
        }
        addSub(sub) {
          this.subs.push(sub)
          console.log('subs', this.subs)
        }
        depend() {
          if (Dep.target) {
            this.addSub(Dep.target)
          }
        }
        notify() {
          this.subs.forEach(sub => sub.update())
        }
      }
    
      class Watcher {
        constructor() {
          Dep.target = this
        }
        update() {
          console.log('更新视图')
          patch()
        }
      }
    
      function Vue(options) {
        this.$data = options.data
        observer(this.$data)
        new Watcher()
      }
    
      var vue = new Vue({
        data: {
          name: 'zcl',
          age: 26
        }
      })
    
      // 先依赖收集
      console.log(vue.$data.name)
      // 置为null,防止重复收集依赖
      Dep.target = null
    
      window.onload = function() {
        const input = document.getElementById('vue-input')
        input.addEventListener('input', function(e) {
          vue.$data.name = e.target.value
        })
      }
    
      // 模拟Vue的模版编译,patch对比更新过程
      function patch() {
        const p = document.getElementById('vue-p')
        p.innerText = vue.$data.name
      }
    
    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92

    # 测试

    <strong>效果</strong>:改变输入框的值,即改变Vue的响应数据,最终触发了更新回调,更新输入框下方P标签的数据

    思路:

    1. Object.defineProperty作数据劫持,getter做依赖收集,收集watcher,getter做通知notify
    2. watcher执行update
    3. 依赖收集后,我把<code>Dep.target</code>清空,是为了防止重复收集依赖。

    100行代码,考察的知识点很多:<code>递归</code>,<code>Vue响应式原理</code>,<code>Object.defineProperty</code>, <code>原生Dom操作</code>,<code>队列</code>

    编辑 (opens new window)
    上次更新: 2025/07/12, 10:46:19
    手写eventEmit类
    手写Vue nextTick

    ← 手写eventEmit类 手写Vue nextTick→

    最近更新
    01
    2024年
    07-20
    02
    2023年
    07-20
    03
    2022年
    07-20
    更多文章>
    Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式