当前位置: 首页 > news >正文

Vue 的双向数据绑定原理

Vue 的双向数据绑定是通过 数据劫持 + 发布-订阅模式 实现的,具体分为以下三个关键机制:

1. 数据劫持(响应式系统)

Vue 使用 Object.defineProperty(Vue 2)或 Proxy(Vue 3)监听数据变化。

Vue 2 实现:

// 简化版数据劫持
function defineReactive(obj, key) {let value = obj[key];const dep = new Dep(); // 依赖收集器Object.defineProperty(obj, key, {get() {if (Dep.target) {dep.addSub(Dep.target); // 收集依赖(Watcher)}return value;},set(newVal) {if (newVal === value) return;value = newVal;dep.notify(); // 通知所有订阅者更新}});
}

Vue 3 改进:

// 使用Proxy实现
const reactive = (target) => {return new Proxy(target, {get(target, key, receiver) {track(target, key); // 依赖收集return Reflect.get(target, key, receiver);},set(target, key, value, receiver) {Reflect.set(target, key, value, receiver);trigger(target, key); // 触发更新return true;}});
};

2. 依赖收集(发布-订阅模式)

Dep 类:管理依赖(一个属性对应一个Dep)Watcher 类:订阅数据变化,触发更新
class Dep {constructor() {this.subs = [];}addSub(sub) {this.subs.push(sub);}notify() {this.subs.forEach(sub => sub.update());}
}class Watcher {update() {// 执行更新(如重新渲染组件)}
}

3. 模板编译(指令解析)

Vue 编译器将模板中的指令(如 v-model)转换为绑定代码:
<input v-model="message">

编译后相当于:

<input :value="message" @input="message = $event.target.value"
>

双向绑定工作流程

初始化阶段:遍历 data 对象,用 Object.defineProperty/Proxy 劫持所有属性编译模板,为每个绑定创建 Watcher数据访问时(触发 getter):当前 Watcher 被添加到属性的 Dep 中数据修改时(触发 setter):Dep 通知所有 Watcher 更新Watcher 触发组件重新渲染

4、Vue 2 vs Vue 3 实现对比

特性Vue 2Vue 3
核心APIObject.definePropertyProxy
监听范围仅能劫持已定义属性动态新增属性自动响应
数组处理需重写数组方法直接监听数组变化
性能递归遍历所有属性惰性监听,按需触发

5、常见问题解答

Q1:为什么 Vue 3 改用 Proxy?

解决 Vue 2 无法检测对象/数组新增属性的问题性能更好(无需递归初始化所有属性)

Q2:v-model 在自定义组件中的原理?

<CustomComponent v-model="value" />

等价于:

<CustomComponent :modelValue="value"@update:modelValue="newVal => value = newVal"
/>

手写极简双向绑定

// 简易版Vue响应式
class MiniVue {constructor(options) {this.$data = options.data;this.observe(this.$data);this.compile(options.el);}observe(data) {Object.keys(data).forEach(key => {let value = data[key];const dep = new Dep();Object.defineProperty(data, key, {get() {Dep.target && dep.addSub(Dep.target);return value;},set(newVal) {value = newVal;dep.notify();}});});}compile(el) {const element = document.querySelector(el);this.walk(element);}walk(node) {if (node.nodeType === 1) { // 元素节点Array.from(node.attributes).forEach(attr => {if (attr.name.startsWith('v-')) {const dir = attr.name.substring(2);if (dir === 'model') {const key = attr.value;node.value = this.$data[key];new Watcher(this.$data, key, val => {node.value = val;});node.addEventListener('input', e => {this.$data[key] = e.target.value;});}}});}// 递归子节点...}
}
通过这种机制,Vue 实现了数据变化自动更新视图、视图输入自动更新数据的双向绑定效果。
http://www.dtcms.com/a/308070.html

相关文章:

  • 自我学习----绘制Mark点
  • 解决Pycharm内存一直升高卡死、反应慢、CPU占用高
  • 《通信原理》学习笔记——第六章
  • IntelliJ IDEA 的常用快捷键
  • Git 详细安装配置教程(Windows版)
  • 以微服务为基础搭建一套脚手架开始前的介绍
  • BGP高级特性之认证
  • python刷题关键记录【常用api使用方法总结,常用函数使用方法】
  • RHEL 8.10 离线安装 Ansible 完整教程
  • 网络基础——路由控制
  • iOS 类存储 与 C# 类存储 的差异
  • 正则化都是放在模型的哪个位置呢?
  • 系统讲解图片格式转换:为什么要转换、怎么转换
  • 数据治理:数字化时代的 “治” 与 “理” 之道 —— 破解企业数据资产困局
  • 【2025/07/31】GitHub 今日热门项目
  • 代码随想录day51图论2
  • Spring MVC体系结构和处理请求控制器
  • 图论:SPFA算法
  • 嵌入式操作系统快速入门(1):快速入门操作系统常见基础概念
  • CMake项目中如何按目录结构分离显示Header和Source文件
  • LPC2132GPIO
  • Ubuntu 内网多台服务器时间同步方案(适用于临时能上外网的环境)
  • 电商作图:解锁“素材裂变”和“产品测款”新姿势
  • Zombie Process
  • Apache Camel 简介
  • STM32 USB 设备中间件 tinyusb
  • 开疆智能Profient转Modbus网关连接MAG8000电池流量计配置案例
  • 快速入门开源项目若依
  • FISCO BCOS Gin调用WeBASE-Front接口发请求
  • 【Kiro Code】Chat 聊天功能