Vue 的双向绑定和响应式系统原理

本文旨在深入探讨 Vue 的双向绑定和响应式系统是如何工作的,并通过原生 JavaScript 代码实现一个简单的响应式系统。

Vue 的双向绑定原理

Vue 的双向绑定允许在 JavaScript 对象和 UI 元素之间建立一个自动的数据同步 。这意味着,当更新 JavaScript 对象的属性值时,绑定的 UI 元素也会相应更新;反之亦然。Vue 通过使用 v-model 指令在表单输入和应用状态之间创建双向绑定。

如何工作?

Vue 实现双向绑定的核心是响应式系统在初始化组件时,Vue 会遍历组件的 data 函数返回的对象,使用 Object.defineProperty() 方法把这些属性全部转为 getter/setter。Vue 内部追踪依赖,在属性被访问和修改时通知变化

当数据发生变化时,Vue 会自动找到依赖这个数据的地方,并自动执行更新。这个过程是通过依赖收集和触发更新实现的

Vue 的响应式系统原理

Vue 的响应式系统基于发布-订阅模式。每个组件实例有一个 watcher 实例,它会在组件渲染过程中记录所有被访问的响应式数据。这些数据通过 getter/setter 来拦截数据的读取和修改操作。

响应式系统的关键步骤:

  1. 依赖收集:在 getter 中收集依赖,即记录当前组件的 watcher。
  2. 派发更新:在 setter 中触发更新,即通知依赖的 watcher 更新视图。

实现一个简单的响应式系统

下面使用原生 JavaScript 实现一个简化版的响应式系统,来深入理解 Vue 响应式原理的精髓。

javascript 复制代码
function defineReactive(obj, key, val) {
    const dep = new Dep();

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            dep.depend();
            return val;
        },
        set: function reactiveSetter(newVal) {
            if (newVal === val) return;
            val = newVal;
            dep.notify();
        }
    });
}

class Dep {
    constructor() {
        this.subscribers = new Set();
    }

    depend() {
        if (Dep.target) {
            this.subscribers.add(Dep.target);
        }
    }

    notify() {
        this.subscribers.forEach(sub => sub());
    }
}

Dep.target = null;

function watcher(myFunc) {
    Dep.target = myFunc;
    myFunc();
    Dep.target = null;
}

// 使用示例
const data = { price: 5, quantity: 2 };
defineReactive(data, 'price', data.price);
defineReactive(data, 'quantity', data.quantity);

let total = 0;

watcher(() => {
    total = data.price * data.quantity;
});

console.log(total); // 输出:10
data.price = 20;
console.log(total); // 输出:40

在这个简单的响应式系统的代码实现过程中,首先定义了 defineReactive 函数,用于把对象的属性转换成可观测的响应式属性。Dep 类用来收集依赖(订阅者)和派发更新(通知订阅者)。watcher 函数模拟了 Vue 中 watcher 的工作原理,它将一个函数转换成响应式的执行环境。

defineReactivedata 对象的属性转换成响应式属性时,每个属性都关联了一个 Dep 实例。这个 Dep 实例负责收集所有依赖于该属性的 watcher,并在属性值发生变化时通知这些 watcher。

然后,通过 watcher 函数创建了一个简单的 watcher,这个 watcher 函数计算 data.pricedata.quantity 的乘积,并将结果赋值给 total。在 watcher 函数执行的过程中,它读取了 data.pricedata.quantity 的值,这两个操作触发了它们各自的 getter。在 getter 中,通过 dep.depend() 调用将当前的 watcher(即 Dep.target 指向的函数)添加到了相应属性的 Dep 实例的订阅者列表中。这样,当之后修改 data.pricedata.quantity 的值时,setter 会被触发,进而调用 dep.notify(),通知所有订阅者(watcher)重新执行,导致 total 被更新。

这个过程演示了 Vue 响应式系统的核心原理:通过依赖收集和派发更新,Vue 能够确保当数据变化时,所有依赖于这些数据的视图都能被自动更新。这种自动追踪变化并响应的机制,极大地简化了动态数据绑定和视图更新的处理,使得开发者可以专注于业务逻辑,而不是繁琐的 DOM 操作和数据同步问题。

总结

Vue 的双向绑定和响应式系统是其最吸引人的特性之一,它们极大地提高了开发效率和应用性能。通过深入理解这些特性的工作原理,开发者可以更好地利用 Vue 提供的强大功能,构建出响应迅速、用户体验优秀的 Web 应用。同时,通过学习并尝试实现简化版的响应式系统,开发者可以加深对这些原理的理解,为解决复杂的开发问题和优化应用性能奠定坚实的基础。

P.S

  • 所谓双向,可以理解成:
  • 第一向:数据驱动视图,通过getter/setter提前设置数据改变之后的回调来完成
  • 第二向:视图到数据,通过事件驱动,通常涉及的是用户交互
相关推荐
发现一只大呆瓜2 小时前
虚拟列表:从定高到动态高度的 Vue 3 & React 满分实现
前端·vue.js·react.js
鱼毓屿御3 小时前
如何给用户添加权限
前端·javascript·vue.js
Java新手村3 小时前
基于 Vue 3 + Spring Boot 3 的 AI 面试辅助系统:实时语音识别 + 大模型智能回答
vue.js·人工智能·spring boot
雯0609~3 小时前
hiprint:实现项目部署与打印3-vue版本-独立出模板设计与模板打印页面
前端·vue.js·arcgis
David凉宸4 小时前
Vue 3 + TS + Vite + Pinia vs Vue 2 + JS + Webpack + Vuex:对比分析
javascript·vue.js·webpack
滕青山4 小时前
Vue项目BMI计算器技术实现
前端·vue.js
boooooooom4 小时前
Pinia必学4大核心API:$patch/$reset/$subscribe/$onAction,用法封神!
javascript·vue.js·面试
wxin_VXbishe4 小时前
C#(asp.net)学员竞赛信息管理系统-计算机毕业设计源码28790
java·vue.js·spring boot·spring·django·c#·php
哈里谢顿4 小时前
Vue 3 入门完全指南:从零构建你的第一个响应式应用
vue.js
三十_A6 小时前
零基础通过 Vue 3 实现前端视频录制 —— 从原理到实战
前端·vue.js·音视频