首页 > 其他分享 >从底层代码理解Vue 响应式系统的核心机制

从底层代码理解Vue 响应式系统的核心机制

时间:2024-09-07 13:49:55浏览次数:8  
标签:Vue target Dep 视图 Watcher 响应 依赖 更新 底层

在 Vue.js 的响应式系统中,依赖收集和变化检测是核心机制,确保了数据的变动能够自动驱动视图更新。在上文我们已经了解了的依赖收集变化检测与更新过程,主要依赖于 WatcherDep 和响应式的 gettersetter 来实现。下面详细从底层代码解释这两个过程

文章目录

1. 依赖收集

依赖收集是指 Vue 在组件渲染过程中,收集那些与视图有关的数据属性,从而将它们与当前的 Watcher 建立关联。具体步骤如下:

  • 激活 Watcher:当一个组件开始渲染时,Vue 会创建一个 Watcher 实例,这个 Watcher 用来追踪组件模板中依赖的响应式数据。

  • 触发 getter 进行依赖收集

    • 在渲染组件时,Vue 会通过访问数据(如 datacomputed)来更新 DOM。此时,响应式数据的 getter 会被触发。
    • getter 中,Vue 会将当前活跃的 Watcher(即正在渲染的组件)添加到该数据属性的 Dep 中,完成依赖收集。这意味着该数据与这个 Watcher 建立了关联。
  • Dep 的作用Dep 是一个依赖管理器,它管理与某个数据属性相关联的所有 Watcher。每个响应式属性都有一个 Dep,在 getter 中会通过 Dep 收集当前的 Watcher

    • 核心目标:为每个被访问的数据属性记录哪些 Watcher 依赖于它。这样,当数据变化时,Vue 能通知与这个数据有关的所有 Watcher
依赖收集的代码示例:
class Dep {
  constructor() {
    this.subs = []; // 订阅者列表,存储 Watcher
  }

  // 添加 Watcher 到订阅者列表
  addSub(watcher) {
    this.subs.push(watcher);
  }

  // 通知所有订阅者更新
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn;
    // 记录当前的 Watcher
    Dep.target = this;
  }

  // 更新视图
  update() {
    this.updateFn();
  }
}

// 通过 getter 收集依赖
function defineReactive(obj, key, val) {
  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.target 是一个非常重要的全局变量,它用于依赖收集阶段,记录当前正在执行的 Watcher 实例。它的作用是在访问响应式数据时,能够将这个响应式数据的依赖(即当前的 Watcher)记录下来,便于后续数据变化时通知相关的 Watcher 进行更新。

2. 变化检测与更新

变化检测与更新发生在某个数据属性的值被修改时。这个过程确保数据的变更能驱动相应的视图更新。

  • 触发 setter:当你修改一个响应式数据属性时,setter 会被触发。在 setter 中,Vue 通过 Dep 来通知所有依赖这个属性的 Watcher,告诉它们数据发生了变化。

  • 通知 Watcher 更新

    • 每个依赖该属性的 Watcher 会通过 Depnotify() 方法被通知。Dep 中存储了所有与该数据属性相关的 Watcher,它会调用每个 Watcherupdate() 方法。
  • 重新渲染视图

    • update() 被调用时,Watcher 会执行其更新函数。这通常会重新执行与视图渲染相关的逻辑,最终触发组件的重新渲染,保证视图与数据保持同步。
变化检测与更新的代码示例:
// 修改响应式数据,触发 setter
data.message = 'Hello Vue.js';

// setter 中触发 notify,通知相关 Watcher 更新视图
function defineReactive(obj, key, val) {
  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(); // 数据变化时,通知 Watcher
      }
    }
  });
}

// Watcher 更新方法
class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn;
    Dep.target = this;
  }

  update() {
    this.updateFn(); // 重新执行视图渲染
  }
}

// 重新渲染视图的逻辑,假设渲染依赖 data.message
const renderWatcher = new Watcher(() => {
  console.log(`视图更新: ${data.message}`);
});

// 修改数据,触发视图更新
data.message = 'Hello Vue.js 3';

3. 使用响应式原理来修改属性并更新实际的 DOM

<div id="app">
  <p id="message">{{ message }}</p>
</div>
<button id="changeMessage">Change Message</button>

<script src="app.js"></script>

// Dep类,用于管理依赖
class Dep {
  constructor() {
    this.subs = [];
  }

  // 添加依赖
  addSub(watcher) {
    this.subs.push(watcher);
  }

  // 通知所有依赖,更新视图
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

// 全局变量,记录当前的 Watcher
Dep.target = null;

// Watcher类,用于执行更新逻辑
class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn;
    Dep.target = this;  // 在实例化时记录当前的 Watcher
    this.update();  // 初始化时调用更新函数
    Dep.target = null;  // 清空 Dep.target,防止后续误用
  }

  // 执行更新函数
  update() {
    this.updateFn();
  }
}

// 响应式处理函数
function defineReactive(obj, key, val) {
  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();  // 通知依赖更新
      }
    }
  });
}

// 创建响应式数据对象
const data = {};
defineReactive(data, 'message', 'Hello Vue.js');

// 获取 DOM 元素
const messageElement = document.getElementById('message');
const button = document.getElementById('changeMessage');

// Watcher,自动更新 DOM 元素
new Watcher(() => {
  messageElement.innerText = data.message;  // 更新视图中的文本
});

// 修改数据时触发视图更新
button.addEventListener('click', () => {
  data.message = 'Hello World';  // 修改数据,自动更新 DOM
});



  • 依赖收集:当 data.message 被访问时,getter 会把当前的 Watcher(通过 Dep.target 保存的)添加到 Dep 的依赖列表中。
  • 变化检测与通知:当 data.message 被修改时,setter 会调用 dep.notify(),通知所有依赖于 message 的 Watcher。
  • 自动更新 DOM:Watcher 的 update() 方法中,我们直接更新 DOM 中的 messageElement.innerText,这样在数据变化时,视图会自动更新。

总结

  • 依赖收集:在组件渲染过程中,Vue 会通过访问响应式数据触发 getter,并将当前正在渲染的 Watcher 与数据属性建立依赖关系。Dep 是一个依赖管理器,负责管理该数据属性与哪些 Watcher 相关。

  • 变化检测与更新:当数据变化时,Vue 会触发数据属性的 setter,通过 Dep 通知所有依赖该属性的 WatcherWatcher 会执行 update() 方法,从而触发视图的重新渲染。

这个过程就是 Vue 响应式系统的核心机制,确保视图和数据之间的自动同步。

标签:Vue,target,Dep,视图,Watcher,响应,依赖,更新,底层
From: https://blog.csdn.net/2401_85770776/article/details/141994741

相关文章

  • SprinBoot+Vue餐饮连锁店管理系统的设计与实现
    目录1项目介绍2项目截图3核心代码3.1Controller3.2Service3.3Dao3.4application.yml3.5SpringbootApplication3.5Vue4数据库表设计5文档参考6计算机毕设选题推荐7源码获取1项目介绍博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w......
  • HTTP协议基本知识点:工作原理、http请求、响应、连接以及缓存机制
    目录一、HTTP概述二、HTTP的版本三、HTTP请求1.请求方法2.请求头四、HTTP响应1.状态码2.响应头五、HTTP持久连接六、缓存机制1.CacheControl2.ETag3.LastModified七、安全性八、使用场景总结 一、HTTP概述 全称:超文本传输协议(HyperTextTransferProtoco......
  • vue 内置组件有哪些
    Vue.js框架提供了一些内置的全局组件,这些组件可以直接在任何Vue应用程序中使用而无需额外注册。以下是一些常用的Vue内置组件:slot-用于内容分发,在组件内部定义插槽区域,允许父组件向这些区域插入内容。template-不渲染任何实际的DOM元素,而是作为一个占位符用于组......
  • 基于nodejs+vue电信CRM系统的设计与实现[程序+论文+开题] 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和电信市场的日益竞争激烈,电信企业面临着前所未有的挑战与机遇。客户关系管理(CRM)作为提升企业核心竞争力的关键手段,其重要性愈发凸显......
  • 基于nodejs+vue电影订票系统[程序+论文+开题] 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着电影产业的蓬勃发展,观众对于观影体验的需求日益多样化与个性化。传统的线下购票方式已难以满足现代人快节奏的生活需求,尤其是在热门影片上映时,排队购票......
  • 基于nodejs+vue电影订票系统设计与实现[程序+论文+开题] 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着互联网的飞速发展,线上服务已成为人们日常生活中不可或缺的一部分,特别是在娱乐消费领域,电影作为大众喜爱的文化娱乐方式之一,其订票方式也经历了从传统线......
  • 基于nodejs+vue电影评论网站系统[程序+论文+开题] 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展和人们生活娱乐方式的多样化,电影已成为大众休闲娱乐的重要组成部分。每年,全球范围内产出数以万计的电影作品,观众对于电影的选择与......
  • VUE0003:Naive UI库:滑动条,单选,多选组件
    1,滑动条,单选,多选组件 <template><n-scrollbarclass="show-scrollbar"><n-spaceclass="map-setting"vertical><n-spacestyle="flex-flow:row;align-items:center;"><n-textclass=&q......
  • 基于ssm+vue盐亭县旅游系统【开题+程序+论文】
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着旅游业的蓬勃发展,地方特色旅游资源的挖掘与整合成为推动区域经济转型升级的重要动力。盐亭县,作为四川省内一个拥有丰富历史文化底蕴与自然风光的......
  • 基于ssm+vue医院挂号系统【开题+程序+论文】
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息化高速发展的今天,医疗服务领域的数字化转型已成为提升医疗效率、优化患者就医体验的重要途径。传统的医院挂号方式往往存在排队时间长、信息更......