【Vue2 ✨】Vue2 入门之旅 · 进阶篇(一):响应式原理

在入门篇中,我们知道 Vue2 的核心思想是 数据驱动视图 。那么 Vue2 是如何实现的呢?本篇将带你深入理解 Vue2 的 响应式原理


目录

  1. 什么是响应式
  2. [Vue2 的实现方式](#Vue2 的实现方式)
  3. [Observer 与数据劫持](#Observer 与数据劫持)
  4. [Dep 与 Watcher 的关系](#Dep 与 Watcher 的关系)
  5. 完整流程
  6. 小结

什么是响应式

所谓响应式:当数据发生变化时,视图会自动更新。

在 Vue2 中,开发者只需要修改 data 中的数据,DOM 就会跟着变化,无需手动操作。

例子:

html 复制代码
<div id="app">
  <p>{{ message }}</p>
  <button @click="message = 'Hello Vue!'">修改</button>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: '初始数据'
  }
})
</script>

点击按钮时,message 改变,页面会自动更新。


Vue2 的实现方式

Vue2 使用 数据劫持 + 发布订阅模式 来实现响应式。

  • 数据劫持 :通过 Object.defineProperty 拦截对象属性的读写。
  • 发布订阅:当数据变化时,通知依赖该数据的地方更新。

Vue3 则改用 Proxy,这里我们只讨论 Vue2。


Observer 与数据劫持

Vue 内部会遍历 data 对象,用 Object.defineProperty 给每个属性加上 getter/setter。

js 复制代码
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get() {
      console.log('获取数据:', key)
      return val
    },
    set(newVal) {
      console.log('设置数据:', key, newVal)
      val = newVal
      // 通知更新
    }
  })
}

const data = {}
defineReactive(data, 'msg', 'Hello')

console.log(data.msg) // 触发 get
data.msg = 'Hi'       // 触发 set

这样就能在属性被访问或修改时执行额外逻辑。


Dep 与 Watcher 的关系

Vue 内部有两个重要角色:

  • Dep(依赖收集器):管理一组 Watcher
  • Watcher(订阅者):代表一个依赖(比如组件的某个渲染函数)

简单代码模拟:

js 复制代码
// 1. 定义 Dep 类(依赖收集器)
class Dep {
  constructor() {
    this.watchers = []; // 存储依赖当前数据的所有 Watcher
  }

  // 收集依赖:记录 Watcher
  addWatcher(watcher) {
    this.watchers.push(watcher);
  }

  // 通知更新:数据变化时,告诉所有 Watcher 执行更新
  notify() {
    this.watchers.forEach(watcher => watcher.update());
  }
}

// 2. 定义 Watcher 类(订阅者)
class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn; // 数据变化时要执行的函数(比如重新渲染)
    // 初始化时主动触发一次依赖收集
    Dep.target = this; // 临时标记当前 Watcher(Vue 内部用这个方式关联 Dep 和 Watcher)
  }

  // 数据变化时执行的方法
  update() {
    this.updateFn(); // 调用传入的更新函数(比如重新渲染视图)
  }
}

// 3. 模拟 Vue 中的数据(比如 data 里的属性)
const data = {
  name: "张三" // 这个属性会对应一个 Dep 实例
};

// 为 data.name 创建对应的 Dep 实例(Vue 会为每个数据属性自动创建 Dep)
const nameDep = new Dep();

// 4. 模拟"组件渲染"场景(依赖 data.name)
// 假设组件渲染时需要读取 data.name,此时会触发依赖收集
function renderComponent() {
  console.log(`渲染视图:姓名是 ${data.name}`);
}

// 创建一个 Watcher 关联"渲染函数"(表示这个渲染依赖数据)
new Watcher(() => {
  renderComponent(); // 当数据变化时,重新执行渲染
});

// 注意:上面 new Watcher 时,会执行一次 renderComponent,此时会读取 data.name
// 读取时会触发 Dep 收集这个 Watcher(Vue 内部通过 getter 实现这一步)
nameDep.addWatcher(Dep.target); // 手动模拟收集(实际由 Vue 自动完成)
Dep.target = null; // 清除标记


// 5. 现在修改数据,观察效果
console.log("--- 修改 name 为 '李四' ---");
data.name = "李四";
// 数据变化时,Dep 通知所有依赖的 Watcher 更新
nameDep.notify(); 
// 输出:"渲染视图:姓名是 李四"(视图自动更新)

关系图:

复制代码
data 属性 ------> Dep ------> Watcher ------> 更新视图

当属性被读取时,Dep 记录下依赖它的 Watcher;

当属性被修改时,Dep 通知所有 Watcher 执行更新。


完整流程

  1. Vue 初始化时,对 data 中的属性调用 Object.defineProperty
  2. 渲染模板时,读取数据,触发 getter,把当前组件的 Watcher 添加到 Dep。
  3. 修改数据时,触发 setter,Dep 通知所有 Watcher。
  4. Watcher 执行更新函数,触发视图重新渲染。

这就是 数据变化 → 视图更新 的完整闭环。


小结

  1. Vue2 响应式基于 Object.defineProperty 实现数据劫持。
  2. 核心机制:Observer(数据劫持) + Dep(依赖收集器) + Watcher(订阅者)
  3. 响应式原理流程:数据劫持 → 依赖收集 → 派发更新。

📗 下一篇进阶文章,我们将学习 虚拟 DOM 与 Diff 算法

相关推荐
狗哥哥2 小时前
微前端路由设计方案 & 子应用管理保活
前端·架构
TT哇3 小时前
【实习 】银行经理端两个核心功能的开发与修复(银行经理绑定逻辑修复和线下领取扫码功能开发)
java·vue.js
前端大卫3 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘3 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare3 小时前
浅浅看一下设计模式
前端
Lee川3 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix3 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人4 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl4 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Grok API 申请及使用
前端