【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 算法

相关推荐
掘根17 小时前
【Protobuf】proto3语法详解1
开发语言·前端·javascript
IT_陈寒17 小时前
SpringBoot 3.2新特性盘点:这5个隐藏功能让你的开发效率翻倍 🚀
前端·人工智能·后端
艾小码17 小时前
从入门到精通:JavaScript异步编程避坑指南
前端·javascript
七度光阴;17 小时前
Web后端登录认证(会话技术)
前端·tlias智能辅助系统
菜鸟una18 小时前
【微信小程序 + map组件】自定义地图气泡?原生气泡?如何抉择?
前端·vue.js·程序人生·微信小程序·小程序·typescript
昔人'20 小时前
`list-style-type: decimal-leading-zero;`在有序列表`<ol></ol>` 中将零添加到一位数前面
前端·javascript·html
岁月宁静1 天前
深度定制:在 Vue 3.5 应用中集成流式 AI 写作助手的实践
前端·vue.js·人工智能
心易行者1 天前
10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
前端
saadiya~1 天前
ECharts 实时数据平滑更新实践(含 WebSocket 模拟)
前端·javascript·echarts
fruge1 天前
前端三驾马车(HTML/CSS/JS)核心概念深度解析
前端·css·html