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

Vue.js 响应式原理与数据绑定

在 Vue.js 中,响应式系统是其核心特性之一,它使得数据的变化能够自动更新到 DOM 上,实现了数据和视图的双向绑定。下面详细介绍 Vue.js 响应式系统的原理以及它是如何实现数据绑定的。

原理概述

Vue.js 的响应式系统主要基于 JavaScript 的 Object.defineProperty() 方法(Vue 2.x)和 ES6 的 Proxy 对象(Vue 3.x)来实现。其核心思想是通过拦截数据对象的属性访问和修改操作,当数据发生变化时,自动触发相应的更新操作,从而更新与之绑定的 DOM。

Vue 2.x 响应式原理及数据绑定实现

  1. Object.defineProperty()方法
    Object.defineProperty()方法可以直接在一个对象上定义一个新属性,或者修改一个现有属性的配置,并返回这个对象。它允许我们劫持对象属性的getter和setter方法,从而实现对属性访问和修改的拦截。
  2. 实现步骤
    • 数据劫持: 在创建 Vue 实例时,Vue 会遍历data选项中的所有属性,使用Object.defineProperty()将这些属性转换为getter/setter。
    • 依赖收集: 当一个getter被触发时,意味着有地方在访问这个属性,Vue 会将这个依赖收集起来。
    • 发布更新: 当一个setter被触发时,意味着属性的值发生了变化,Vue 会通知所有依赖该属性的地方进行更新。
  3. 示例代码
// 模拟Vue的响应式系统
function defineReactive(obj, key, val) {
    // 创建一个Dep对象,用于收集依赖
    const dep = new Dep();
    Object.defineProperty(obj, key, {
        get() {
            // 收集依赖
            if (Dep.target) {
                dep.addSub(Dep.target);
            }
            return val;
        },
        set(newVal) {
            if (newVal !== val) {
                val = newVal;
                // 通知所有依赖更新
                dep.notify();
            }
        }
    });
}
// Dep类,用于管理依赖
class Dep {
    constructor() {
        this.subs = [];
    }
    addSub(sub) {
        this.subs.push(sub);
    }
    notify() {
        this.subs.forEach(sub => sub.update());
    }
}
// Watcher类,用于订阅数据变化
class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm;
        this.cb = cb;
        Dep.target = this;
        this.getter = parsePath(expOrFn);
        this.value = this.get();
        Dep.target = null;
    }
    get() {
        return this.getter.call(this.vm, this.vm);
    }
    update() {
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue);
    }
}
// 解析路径
function parsePath(path) {
    const segments = path.split('.');
    return function (obj) {
        for (let i = 0; i < segments.length; i++) {
            if (!obj) return;
            obj = obj[segments[i]];
        }
        return obj;
    };
}
// 使用示例
const data = {
    message: 'Hello, Vue!'
};
defineReactive(data, 'message', data.message);
const updateDOM = function (newValue, oldValue) {
    console.log(`数据更新:${oldValue} -> ${newValue}`);
};
new Watcher(data, 'message', updateDOM);
// 修改数据
data.message = 'Hello, World!';

Vue 3.x 响应式原理及数据绑定实现

  1. Proxy对象
    ES6 引入的Proxy对象可以创建一个对象的代理,从而实现对该对象的基本操作的拦截和自定义。相比于Object.defineProperty(),Proxy可以直接监听对象的属性变化,并且可以监听对象的新增和删除属性。
  2. 实现步骤
    • 创建代理: 在创建 Vue 实例时,Vue 会使用Proxy对象对data选项中的对象进行代理。
    • 依赖收集和发布更新: 与 Vue 2.x 类似,当访问代理对象的属性时,会触发getter进行依赖收集;当修改属性时,会触发setter通知所有依赖进行更新。
  3. 示例代码
// 模拟Vue 3.x的响应式系统
function reactive(target) {
    const handler = {
        get(target, key) {
            // 收集依赖
            track(target, key);
            return target[key];
        },
        set(target, key, value) {
            target[key] = value;
            // 通知更新
            trigger(target, key);
            return true;
        }
    };
    return new Proxy(target, handler);
}
// 依赖收集
const targetMap = new WeakMap();
function track(target, key) {
    if (!Dep.target) return;
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
        depsMap.set(key, (dep = new Set()));
    }
    dep.add(Dep.target);
}
// 发布更新
function trigger(target, key) {
    const depsMap = targetMap.get(target);
    if (!depsMap) return;
    const dep = depsMap.get(key);
    if (dep) {
        dep.forEach(sub => sub.update());
    }
}
// Watcher类,用于订阅数据变化
class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm;
        this.cb = cb;
        Dep.target = this;
        this.getter = parsePath(expOrFn);
        this.value = this.get();
        Dep.target = null;
    }
    get() {
        return this.getter.call(this.vm, this.vm);
    }
    update() {
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue);
    }
}
// 解析路径
function parsePath(path) {
    const segments = path.split('.');
    return function (obj) {
        for (let i = 0; i < segments.length; i++) {
            if (!obj) return;
            obj = obj[segments[i]];
        }
        return obj;
    };
}
// 使用示例
const data = reactive({
    message: 'Hello, Vue 3!'
});
const updateDOM = function (newValue, oldValue) {
    console.log(`数据更新:${oldValue} -> ${newValue}`);
};
new Watcher(data, 'message', updateDOM);
// 修改数据
data.message = 'Hello, World 3!';

总结

Vue.js 的响应式系统通过Object.defineProperty()(Vue 2.x)或Proxy对象(Vue 3.x)实现了数据劫持,通过依赖收集和发布更新机制实现了数据绑定,使得数据的变化能够自动更新到 DOM 上。


http://www.kler.cn/a/544011.html

相关文章:

  • LeetCode3. 无重复字符的最长字串(滑动窗口)
  • JavaScript相关知识点
  • ThinkPHP8视图赋值与渲染
  • 在 Windows 系统中如何快速进入安全模式的两种方法
  • Java+vue前后端分离项目集群部署
  • 深度剖析责任链模式
  • 4. React 中的 CSS
  • Visual Studio 中的键盘快捷方式
  • 【Leetcode 每日一题】1760. 袋子里最少数目的球
  • 【C++学习笔记】if 和 if constexpr
  • Openssl的使用,CA证书,中间证书,服务器证书的生成与使用
  • 如何设计一个高效、稳定的秒杀系统?
  • 【AI】增长迅猛的DeepSeek
  • 《qt+easy3d 网格读取》
  • [Do374]ansible-nagivator考前整理
  • 探索边缘计算网关在优化交通信号控制中的关键角色
  • 【C】链表算法题7 -- 环形链表||
  • HARCT 2025 分论坛9:专用设备和机器人系统
  • 爬虫抓取过程的详细步骤
  • 自动驾驶,不同摄像头安装pitch角度, 同一个模型, 对单目深度精度有影响吗...
  • zyNo.22
  • 基于STM32的ADS1230驱动例程
  • 01、单片机上电后没有正常运行怎么办
  • docker快速部署oracle11g
  • Android10 Framework系列 需求定制(一)修改按键映射相关,顺便看了看按键事件分发
  • 上位机知识篇---SSHSCP密钥与密钥对