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 的工作原理,它将一个函数转换成响应式的执行环境。

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

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

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

总结

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

P.S

  • 所谓双向,可以理解成:
  • 第一向:数据驱动视图,通过getter/setter提前设置数据改变之后的回调来完成
  • 第二向:视图到数据,通过事件驱动,通常涉及的是用户交互
相关推荐
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 2 章 响应式内幕(一):`effect` 与依赖追踪机制
前端·vue.js·开源
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 06 章 编译器内幕(二):`transform` 管线与结构指令
前端·vue.js·开源
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 09 章 编译器内幕(五):compiler-sfc的SFC解析与<script setup> 编译
前端·vue.js·开源
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 17 章 运行时内幕(五):`runtime-dom`、自定义元素与内置组件
前端·vue.js·开源
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 13 章 运行时组件系统:实例、props、emit、slots 与生命周期
前端·vue.js·开源
鶴哥只手遮天3 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 10 章 compiler-sfc(二)
前端·vue.js·开源
will_net3 小时前
Vue IIS部署 api 访问当前站点 代理跳转真实API 问题
前端·javascript·vue.js
定栓3 小时前
Webpack迁到Vite第十节:vite.config.js 开发与构建流转全链路
vue.js·webpack·vite