vue 中的数据代理

news2025/5/12 19:59:11

在 Vue 中,数据代理(Data Proxy) 是 Vue 实现 MVVM 模式 的关键技术之一。Vue 使用数据代理让你可以通过 this.message 访问 data.message,而不需要写 this.data.message —— 这大大简化了模板和逻辑代码。

我们来深入理解它的本质、实现原理和源码体现。

🔹 什么是数据代理?

Vue 实例将 data、props、computed 等属性“代理”到 vm(Vue 实例)自身上,从而让我们能直接通过 this.xx 访问,而不必每次访问 this._data.xx。

✅ 示例:数据代理的使用效果

const vm = new Vue({
  data: {
    message: 'hello'
  }
})

console.log(vm.message) // 实际上是 vm._data.message

🧠 数据代理的实现原理(以 Vue 2 为例)

Vue 2 中通过 Object.defineProperty 实现数据代理。

👉 源码简化模拟:

function proxy(target, sourceKey, key) {
  Object.defineProperty(target, key, {
    get() {
      return target[sourceKey][key]
    },
    set(val) {
      target[sourceKey][key] = val
    }
  })
}

Vue 实例化时,会遍历 data 中的所有属性,调用 proxy(vm, ‘_data’, key) 将它们挂载到实例 vm 上。

🔧 Vue 2 中相关源码(core/instance/state.js)

function initData(vm) {
  const data = vm.$options.data
  vm._data = typeof data === 'function' ? data.call(vm, vm) : data || {}

  // 数据代理
  const keys = Object.keys(vm._data)
  let i = keys.length
  while (i--) {
    proxy(vm, '_data', keys[i])
  }

  observe(vm._data)
}

📌 Vue 3 的处理方式

Vue 3 中由于响应式改为使用 Proxy,所以数据代理并不是必须通过 defineProperty 显式定义,而是由 reactive() 和 ref() 本身提供的响应式代理能力。

import { reactive } from 'vue'

const state = reactive({ count: 0 })

console.log(state.count) // 已被代理,无需显式挂载到组件实例上

Vue 3 更加模块化,组件实例并不自动把数据代理到 this 上,尤其在

🧠 为什么要用数据代理?

简化访问:

  • 不用写 this._data.xxx,直接 this.xxx

实现双向绑定:

  • v-model 语法简洁,背后依赖于代理 + 响应式

方便调试和语法提示:

  • IDE 能提示实例属性;不用深层嵌套对象路径

🔄 数据代理 vs 响应式代理

特性数据代理响应式代理
实现技术Object.defineProperty(Vue 2)Proxy(Vue 3)
目的让你能通过 this.key 访问 data.key追踪依赖、触发视图更新
是否双向绑定否,单纯转发 getter/setter是,配合响应式系统自动更新视图
使用位置Vue 实例、组件所有响应式状态(ref、reactive)

✅ 小结

  • 数据代理是为了简化访问方式,不是响应式的根本机制。
  • Vue 2 中通过 Object.defineProperty 将 data 的属性代理到 Vue 实例上。
  • Vue 3 中响应式是通过 Proxy 实现,数据代理的需求被弱化(但原理仍然存在)。
  • 它是 MVVM 中 ViewModel 作为桥梁的一部分。

小型 Vue 响应式系统(含数据代理)

// 1. 数据代理
function proxy(vm, sourceKey, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[sourceKey][key]
    },
    set(val) {
      vm[sourceKey][key] = val
    }
  })
}

// 2. 响应式转换(核心 Observer)
function defineReactive(obj, key, val) {
  const dep = new Dep()

  Object.defineProperty(obj, key, {
    get() {
      dep.depend()
      return val
    },
    set(newVal) {
      val = newVal
      dep.notify()
    }
  })
}

// 3. 对整个 data 对象做响应式处理
function observe(obj) {
  Object.keys(obj).forEach(key => defineReactive(obj, key, obj[key]))
}

// 4. 依赖收集器
class Dep {
  constructor() {
    this.subscribers = new Set()
  }

  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect)
    }
  }

  notify() {
    this.subscribers.forEach(effect => effect())
  }
}

let activeEffect = null
function watchEffect(effect) {
  activeEffect = effect
  effect() // 立即执行一次
  activeEffect = null
}

// 5. 模拟 Vue 实例
function Vue(options) {
  this._data = options.data
  observe(this._data)

  // 代理 _data 到 this
  Object.keys(this._data).forEach(key => {
    proxy(this, '_data', key)
  })
}
// 创建 Vue 实例
const vm = new Vue({
  data: {
    message: 'Hello',
    count: 1
  }
})

// 绑定“视图更新”逻辑
watchEffect(() => {
  console.log('视图更新:', vm.message, vm.count)
})

// 修改数据,自动触发“视图更新”
vm.message = 'Hello Vue'
vm.count++

Vue 3 响应式系统简易实现(模拟核心功能)

// 1. 依赖收集器
const targetMap = new WeakMap()
let activeEffect = null

function track(target, key) {
  if (!activeEffect) return

  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)
}

function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  const deps = depsMap.get(key)
  if (deps) {
    deps.forEach(effect => effect())
  }
}

// 2. 创建响应式对象
function reactive(target) {
  return new Proxy(target, {
    get(obj, key) {
      track(obj, key)
      return Reflect.get(obj, key)
    },
    set(obj, key, value) {
      const result = Reflect.set(obj, key, value)
      trigger(obj, key)
      return result
    }
  })
}

// 3. 注册副作用(自动运行函数)
function effect(fn) {
  activeEffect = fn
  fn()
  activeEffect = null
}
const state = reactive({
  count: 0,
  name: 'Vue3'
})

effect(() => {
  console.log('视图更新:', state.count, state.name)
})

state.count++     // 触发更新
state.name = 'Vue3 Proxy'

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】学习、项目时Debug总结

这里写目录标题 1. 内存问题1.1. 内存泄漏1.1.1. 内存泄漏案例检查方法1.1.2. 主线程提前退出导致【控】1.1.3. PostThreadMessage失败导致的内存泄漏**【控】**1.1.4. SendMessage 时关闭客户端【控】1.1.5. 线程机制导致【**控】**1.1.6. exit(0)导致【…

26考研——中央处理器_指令流水线_指令流水线的基本概念 流水线的基本实现(5)

408答疑 文章目录 六、指令流水线指令流水线的基本概念流水线的基本实现流水线设计的原则流水线的逻辑结构流水线的时空图表示 八、参考资料鲍鱼科技课件26王道考研书 六、指令流水线 前面介绍的指令都是在单周期处理机中采用串行方法执行的,同一时刻 CPU 中只有一…

AI Agent-基础认知与架构解析

定义 AI Agent 可以理解为一种具备感知、决策和行动能力的智能实体,能够在复杂的环境中自主运行,并根据环境变化动态调整自身行为,以实现特定目标。与传统的人工智能程序相比,AI Agent 具有更强的自主性、交互性和适应性。它不仅能…

C语言--字符函数

C语言--字符函数 一、字符函数1.1 iscntrl1.2 isspace1.3 isdigit1.4 isxdigit1.5 islower1.6 isupper1.7 isalpha1.8 isalnum1.9 ispunct1.10 isgraph1.11 isprint 在编程的过程中,我们会经常处理字符,为了方便操作,C语言标准库中提供了一系…

菜鸟之路Day30一一MySQL之DMLDQL

菜鸟之路Day30一一MySQL之DML&DQL 作者:blue 时间:2025.5.8 文章目录 菜鸟之路Day30一一MySQL之DML&DQL一.DML0.概述1.插入语句(insert)2.更新语句(update)3.删除语句(delete&#xf…

基 LabVIEW 的多轴电机控制系统

在工业自动化蓬勃发展的当下,多轴伺服电机控制系统的重要性与日俱增,广泛应用于众多领域。下面围绕基于 LabVIEW 开发的多轴伺服电机控制系统展开,详细阐述其应用情况。 一、应用领域与场景 在 3D 打印领域,该系统精确操控打印头…

《Go小技巧易错点100例》第三十二篇

本期分享: 1.sync.Map的原理和使用方式 2.实现有序的Map sync.Map的原理和使用方式 sync.Map的底层结构是通过读写分离和无锁读设计实现高并发安全: 1)双存储结构: 包含原子化的 read(只读缓存,无锁快…

需求分析阶段测试工程师主要做哪些事情

在软件测试需求分析阶段,主要围绕确定测试范围、明确测试目标、细化测试内容等方面开展工作,为后续测试计划的制定、测试用例的设计以及测试执行提供清晰、准确的依据。以下是该阶段具体要做的事情: 1. 需求收集与整理 收集需求文档&#x…

项目模拟实现消息队列第二天

消息应答的模式 1.自动应答: 消费者把这个消息取走了,就算是应答了(相当于没有应答) 2.手动应答: basicAck方法属于手动应答(消费者需要主动调用这个api进行应答) 小结 1.需要实现生产者,broker server,消费者这三个部分的 2.针对生产者和消费…

5.Redission

5.1 前文锁问题 基于 setnx 实现的分布式锁存在下面的问题: 重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如 HashTable 这样的代码中,他的方法都是使用 sync…

dify 部署后docker 配置文件修改

1:修改 复制 ./dify/docker/.env.example ./dify/docker/.env 添加一下内容 # 启用自定义模型 CUSTOM_MODEL_ENABLEDtrue# 将OLLAMA_API_BASE_URL 改为宿主机的物理ip OLLAMA_API_BASE_URLhttp://192.168.72.8:11434# vllm 的 OPENAI的兼容 API 地址 CUSTOM_MODE…

数据结构——排序(万字解说)初阶数据结构完

目录 1.排序 2.实现常见的排序算法 2.1 直接插入排序 ​编辑 2.2 希尔排序 2.3 直接选择排序 2.4 堆排序 2.5 冒泡排序 2.6 快速排序 2.6.1 递归版本 2.6.1.1 hoare版本 2.6.1.2 挖坑法 2.6.1.3 lomuto前后指针 2.6.1.4 时间复杂度 2.6.2 非递归版本 2.7 归并排序…

快速入门深度学习系列(3)----神经网络

本文只针对图进行解释重要内容 这就是入门所需要掌握的大部分内容 对于不懂的名词或概念 你可以及时去查 对于层数 标在上面 对于该层的第几个元素 标在下面 输入层算作第0层 对于第一层的w b 参数 维度如下w:4*3 b:4*1 这个叫做神经元 比如对于第一层的神经元 这里说的很…

在线工具源码_字典查询_汉语词典_成语查询_择吉黄历等255个工具数百万数据 养站神器,安装教程

在线工具源码_字典查询_汉语词典_成语查询_择吉黄历等255个工具数百万数据 养站神器,安装教程 资源宝分享:https://www.httple.net/154301.html 一次性打包涵盖200个常用工具!无论是日常的图片处理、文件格式转换,还是实用的时间…

Linux 阻塞和非阻塞 I/O 简明指南

目录 声明 1. 阻塞和非阻塞简介 2. 等待队列 2.1 等待队列头 2.2 等待队列项 2.3 将队列项添加/移除等待队列头 2.4 等待唤醒 2.5 等待事件 3. 轮询 3.1 select函数 3.2 poll函数 3.3 epoll函数 4. Linux 驱动下的 poll 操作函数 声明 本博客所记录的关于正点原子…

Java开发经验——阿里巴巴编码规范经验总结2

摘要 这篇文章是关于Java开发中阿里巴巴编码规范的经验总结。它强调了避免使用Apache BeanUtils进行属性复制,因为它效率低下且类型转换不安全。推荐使用Spring BeanUtils、Hutool BeanUtil、MapStruct或手动赋值等替代方案。文章还指出不应在视图模板中加入复杂逻…

机器人手臂“听不懂“指令?Ethercat转PROFINET网关妙解通信僵局

机器人手臂"听不懂"指令?Ethercat转PROFINET网关妙解产线通信僵局 协作机器人(如KUKA iiWA)使用EtherCAT控制,与Profinet主站(如西门子840D CNC)同步动作。 客户反馈:基于Profinet…

深度学习 CNN

CNN 简介 什么是 CNN? 卷积神经网络(Convolutional Neural Network)是专为处理网格数据(如图像)设计的神经网络。核心组件: 卷积层 :提取局部特征(如边缘、纹理)通过卷…

MySQL索引原理以及SQL优化(二)

目录 1. 索引与约束 1.1 索引是什么 1.2 索引的目的 1.3 索引分类 1.3.1 数据结构 1.3.2 物理存储 1.3.3 列属性 1.3.4 列的个数 1.4 主键的选择 1.5 索引使用场景 1.6 索引的底层实现 1.6.1 索引存储 1.6.2 页 1.6.3 B 树 1.6.4 B 树层高问题 1.6.5 自增 id 1.7 innod…

MATLAB中矩阵和数组的区别

文章目录 前言环境配置1. 数据结构本质2. 运算规则(1)基本运算(2)特殊运算 3. 函数与操作4. 高维支持5. 创建方式 前言 在 MATLAB 中,矩阵(Matrix) 和 数组(Array) 的概…