请谈谈 Vue 中的响应式原理,如何实现?

一、Vue2响应式原理:Object.defineProperty的利与弊

实现原理

// 数据劫持核心实现
function defineReactive(obj, key, val) {
  const dep = new Dep(); // 依赖收集容器
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 当前Watcher实例
        dep.addSub(Dep.target); // 收集依赖
      }
      return val;
    },
    set(newVal) {
      if (val === newVal) return;
      val = newVal;
      dep.notify(); // 触发更新
    }
  });
}

// 遍历对象属性实现响应式
function observe(data) {
  Object.keys(data).forEach(key => {
    defineReactive(data, key, data[key]);
  });
}

// 使用示例
const data = { count: 0 };
observe(data);

典型问题

  1. 无法检测新增属性
data.newProp = 'test'; // 不会触发更新
// 必须使用 Vue.set(data, 'newProp', 'test')
  1. 数组操作需要特殊处理
// 直接修改数组下标无效
data.arr[0] = 1; // 不触发更新
// 必须使用变异方法:push/pop/splice等
data.arr.splice(0, 1, 1);

二、Vue3响应式原理:Proxy的降维打击

实现原理

function reactive(obj) {
  return new Proxy(obj, {
    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;
    }
  });
}

// 使用示例
const state = reactive({ count: 0 });
state.newProp = 'test'; // 直接生效!
state.arr[0] = 1; // 直接生效!

优势对比

特性Vue2(defineProperty)Vue3(Proxy)
新增属性监听❌ 需要Vue.set✅ 原生支持
数组操作❌ 需特殊方法✅ 原生支持
嵌套对象性能❌ 递归劫持✅ 按需代理

三、日常开发建议与避坑指南

1. 数据操作规范
// Vue2正确姿势
this.$set(this.obj, 'newKey', value);
this.arr.splice(index, 1, newValue);

// Vue3正确姿势(直接操作)
state.obj.newKey = value;
state.arr[index] = newValue;
2. 性能优化技巧
// 避免深层响应式(Vue3)
import { shallowRef } from 'vue';
const bigObject = shallowRef({ ... }); // 只跟踪.value变化

// 计算属性缓存
const doubleCount = computed(() => count.value * 2);

// 批量更新(Vue3)
import { nextTick } from 'vue';
async function batchUpdate() {
  state.a = 1;
  state.b = 2;
  await nextTick(); // DOM更新完成
}
3. 典型错误示例
// 错误1:解构丢失响应式(Vue3)
const { count } = reactiveObj; // ❌ 丢失响应式
const count = toRef(reactiveObj, 'count'); // ✅ 正确方式

// 错误2:异步更新陷阱
setTimeout(() => {
  state.count++; // 可能触发多次渲染
}, 100);

// 正确做法(Vue3)
watchEffect(() => {
  // 自动追踪依赖
  console.log(state.count);
});

四、响应式系统设计启示

  1. 依赖收集流程

    • 组件渲染时触发getter
    • 将当前Watcher存入Dep
    • 数据变更时通过Dep通知所有Watcher
  2. 更新队列机制

    // 伪代码实现
    let queue = [];
    function queueWatcher(watcher) {
      if (!queue.includes(watcher)) {
        queue.push(watcher);
        nextTick(flushQueue);
      }
    }
    function flushQueue() {
      queue.forEach(watcher => watcher.run());
      queue = [];
    }

五、面试高频问题参考

  1. Vue2/3响应式实现差异的本质原因是什么?

    • 答:Object.defineProperty的局限性 vs Proxy的语言层支持
  2. 为什么Vue3放弃defineProperty?

    • 答:无法处理Map/Set等新数据结构、数组操作限制、性能开销大
  3. 如何实现自定义响应式系统?

    • 参考思路:Proxy + 依赖收集 + 调度器设计

总结建议

  • 项目选型:新项目直接用Vue3,老项目逐步迁移
  • 开发习惯:避免深层嵌套数据结构,合理使用shallowRef
  • 调试技巧:利用Vue Devtools观察依赖关系
  • 进阶学习:阅读@vue/reactivity源码(仅1800行)

响应式系统是Vue的核心竞争力,理解其实现原理能帮助开发者写出更高效可靠的代码。建议结合项目实际,多实践不同场景下的数据流管理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[C语言]指针进阶压轴题

下面代码打印结果是什么&#xff1f; #include<stdio.h> int main() {char* c[] { "ENTER","NEW","POINT","FIRST" };char** cp[] { c 3,c 2,c 1,c };char*** cpp cp;printf("%s\n", **cpp);printf("%s\n…

DeepSeek 助力 Vue 开发:打造丝滑的点击动画(Click Animations)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

SpringBoot论坛网站 – 功能详解与部署教程

项目概述 《SpringBoot的论坛网站》是一个基于SpringBoot框架开发的现代化论坛平台&#xff0c;旨在为用户提供一个便捷的交流空间。该项目不仅功能丰富&#xff0c;还具备良好的扩展性和易用性&#xff0c;适合用于学习、分享和讨论各类话题。以下是项目的核心功能模块和部署…

SpringSecurity初始化的本质

一、对SpringSecurity初始化的几个疑问 通过前面第一次请求访问的分析我们明白了一个请求就来后的具体处理流程 对于一个请求到来后会通过FilterChainProxy来匹配一个对应的过滤器链来处理该请求。那么这里我们就有几个疑惑。 FilterChainProxy什么时候创建的?过滤器链和对应的…

【大模型系列篇】DeepSeek-R1如何通过强化学习有效提升大型语言模型的推理能力?

如何通过强化学习&#xff08;RL&#xff09;有效提升大型语言模型&#xff08;LLM&#xff09;的推理能力&#xff1f; 《DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning》由DeepSeek-AI团队撰写&#xff0c;主要介绍了他们开发的第一代…

无缝对接[系列2]:在VSCode中继续接入本地DeepSeek的完整指南---实现代码协助编写~

无缝对接&#xff1a;在VSCode中继续接入本地DeepSeek的完整指南 在上一篇文章中&#xff0c;我们已经详细介绍了如何成功部署本地 DeepSeek&#xff0c;使得开发者能够在本地环境中高效地进行深度学习和数据分析工作。 部署完成后&#xff0c;如何在 Visual Studio Code (VS…

linux5-多任务--进程fork()

一.多任务&#xff1a;让系统具备同时处理多个任务的能力 1.如何实现多任务 1.1进程&#xff1a;操作系统上正在运行的程序&#xff0c;需要消耗内存和CPU 1.1.1 进程的生存周期&#xff1a;创建、调度、消亡 1.1.1.1进程的创建&#xff1a;每个进程被创建时&#xff0c;操作…

原生稀疏注意力机制(NSA):硬件对齐且可原生训练的稀疏注意力机制-论文阅读

摘要 长上下文建模对于下一代语言模型至关重要&#xff0c;但标准注意力机制的高计算成本带来了巨大的计算挑战。稀疏注意力提供了一种在保持模型能力的同时提高效率的有前途的方向。本文提出了一种名为 NSA&#xff08;原生可训练稀疏注意力机制&#xff09; 的方法&#xff…

C++ Primer 库-IO类

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

FlutterAssetsGenerator插件的使用

在Plugins中找到FlutterAssetsGenerator插件&#xff0c;点击安装。 更改生成的资源索引类可以修改名字。 在根目录下创建assets/images文件夹&#xff0c;用于存储图片。 点击images文件夹&#xff0c;鼠标右键点击Flutter&#xff1a;Configuring Paths&#xff0c;pub…

网络安全与防范

1.重要性 随着互联网的发达&#xff0c;各种WEB应用也变得越来越复杂&#xff0c;满足了用户的各种需求&#xff0c;但是随之而来的就是各种网络安全的问题。了解常见的前端形式和保护我们的网站不受干扰是我们每个优秀fronter必备的技能。 2.分类 XSS干扰 CSRF干扰 网络劫持干…

【算法】----多重背包问题I,II(动态规划)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

超低失真、超高清晰度的远心工业镜头

随着机器视觉技术的不断提高&#xff0c;工业生产应用机器视觉系统也越来越广泛&#xff0c;大大提高的工厂的效率&#xff0c;而有时候采集的图像有些扭曲变形&#xff0c;也就是失真&#xff0c;图像失真又叫“畸变”&#xff0c;远心镜头就是为纠正传统工业镜头视差而设计一…

JWT认证机制

Session认证机制中需要配合cookie才能实现&#xff0c;由于cookie默认不支持跨域访问&#xff0c;当涉及到前端跨域请求后端接口时&#xff0c;需要做很多额外的配置&#xff0c;才能实现跨域session认证。所以这里不推荐使用session身份认证机制&#xff0c;一般推荐使用jwt认…

嵌入式八股文(四)计算机网络篇

第一章 基础概念 1. 服务 指网络中各层为紧邻的上层提供的功能调用,是垂直的。包括面向连接服务、无连接服务、可靠服务、不可靠服务。 2. 协议 是计算机⽹络相互通信的对等层实体之间交换信息时必须遵守的规则或约定的集合。⽹络协议的三个基本要素:语法、…

数据结构——单向循环链表、双链表、双向循环链表

目录 一、单向循环链表 1.1 单向循环链表的概念 1.2 单向循环链表的操作 1.2.1 单向循环链表的创建 1.2.2 单向循环链表的头插 1.2.3 单向循环链表的遍历 1.2.4 单向循环链表的头删 1.2.5 单向循环链表的尾插 1.2.6 单向循环链表的尾删 1.2.7 约瑟夫环 1.3 单向循环列表所有程…

dify安装

官网教程 https://github.com/langgenius/dify/blob/main/README_CN.md 1、下载源码 git clone https://github.com/langgenius/dify.git 2、进入docker目录 cd dify cd docker cp .env.example .env修改nginx对外端口配置 修改为9000 最后执行&#xff1a;docker compo…

使用Termux将安卓手机变成随身AI服务器(page assist连接)

通过以下方法在安卓手机上运行 Ollama 及大模型&#xff0c;无需 Root 权限&#xff0c;具体方案如下&#xff1a; 通过 Termux 模拟 Linux 环境运行 核心工具&#xff1a; 安装 &#xff08;安卓终端模拟器&#xff09;()]。借助 proot-distro 工具安装 Linux 发行版&#xf…

C++ STL中的reverse/unique/sort/lower_bound/upper_bound函数使用

本文主要涉及以下几个函数&#xff1a; reverse&#xff1a;反转序列。unique&#xff1a;移除相邻重复元素。sort&#xff1a;对序列进行排序。lower_bound 和 upper_bound&#xff1a;查找目标值的边界位置。头文件均为<algorithm> 1. reverse 功能&#xff1a;反转指…

QT QLabel加载图片等比全屏自适应屏幕大小显示

最近在工作项目中,遇到一个需求: 1.使用QLabel显示一张图片; 2.当点击这个QLabel时,需要全屏显示;但不能改变原来的尺寸; 3.当点击放大后的QLabel时,恢复原有大小. 于是乎,就有了本篇博客,介绍如何实现这样的功能. 一、演示效果 在一个水平布局中&#xff0c;添加两个Lable用…