Vue2 的 数据响应式原理

响应式数据的最终目标,是当对象本身或者对象属性发生变化时,将会运行一些函数来触发视图同步更新,其中最常见的就是 render 函数。

在具体实现上,vue 用到了几个核心部件:

  1. 数据转换器:Observer
  2. 依赖收集器:Dep(Dependency)
  3. 视图观察者:Watcher
  4. 更新调度器:Scheduler

Observer

目标:把一个普通对象转换为响应式对象。

方式:通过 Object.defineProperty 把每个对象的属性转换为带有 gettersetter 的属性,因此当访问或设置属性时,vue 可以做依赖收集。

细节:此过程发生在 beforeCreate 之后,created 之前;会递归遍历对象当前的所有属性,但无法对之后动态添加或删除的属性进行转换,因此提供了 $set$delete 方法供开发者使用;对于数组,vue 改变了其隐式原型 __proto__ 属性,重写了操作数组的方法以满足响应式。

Dep

目标:在访问或设置响应式对象的属性时,进行依赖收集或派发更新。

方式:vue 会为响应式对象中的对象本身、每个属性、数组本身创建一个 Dep 实例;访问属性时会运行 getter 函数,通过 dep.depend() 方法收集依赖到依赖列表;设置属性时会运行 setter 函数,通过 dep.notify() 方法派发更新给依赖列表中的每一项。

Watcher

目标:虽然 Dep 负责收集依赖,但它本身并不能直接触发更新或执行具体的逻辑。Watcher 的作用是将数据变化与具体的更新逻辑连接起来。

方式:当要运行一个函数时,vue 不直接执行该函数,而是创建一个 watcher 对象,把函数交给 watcher 去执行,该 watcher 会保存到一个全局变量中,在函数执行过程中,如果发生了依赖收集,Dep 就会把当前活跃的 watcher 收集到依赖列表,当 Dep 派发更新时,就会通知记录下来的所有 watcher 去重新渲染。

细节:每一个 vue 组件实例,都至少对应一个 watcher 实例,该 watcher 中记录了该组件的 render 函数;watcher 首先会把 render 函数运行一次以进行依赖收集,提供上下文环境(通过 Dep.target),使得 Dep 知道当前应该收集哪个 Watcher 作为依赖。当数据变化时 dep 就会通知该 watcher,从而重新运行 render 函数进行页面重新渲染并且重新对当前状态进行依赖收集。

Scheduler

目标:解决大量相同 watcher 造成的频繁渲染。

方式:watcher 收到派发更新的通知后,不会立即执行对应的函数,而是把自己交给 scheduler 去执行,scheduler 会维护一个执行队列,队列中同一个 watcher 仅会存在一次,且所有的 watcher 会通过 nextTick 方法放入到事件循环的微队列中去异步执行。

细节:nextTick 是通过 Promise 完成的,当响应式数据变化时,render 函数的执行是异步的。

相关推荐
戒不掉的伤怀几秒前
electron打包vue2项目流程
前端·javascript·electron
CreatorRay几秒前
前端面经分享(25/03/26)
前端·javascript·面试
Three~stone28 分钟前
Vue学习笔记集--reactive和ref函数
vue.js·笔记·学习
掘金安东尼28 分钟前
推荐!高效灵活的、可集成的、文本化绘图工具 —— PlantUML
前端·javascript·github
哀木29 分钟前
哈哈,commit + push 发布之后看见忘了 add 🤡
前端
古蓬莱掌管玉米的神35 分钟前
基于docker本地搭建Dify详细教程
前端·javascript
要天天开心啊39 分钟前
vue路由缓存问题
前端·vue.js·缓存
掘金一周1 小时前
对话即服务:Spring Boot整合MCP让你的CRUD系统秒变AI助手 | 掘金一周 3.28
前端·人工智能·ai编程
Ch1oy1 小时前
解锁 JavaScript DOM:节点操作的核心方法与最佳实践
开发语言·前端·javascript·ecmascript·html5
Jiude1 小时前
🚨两月没动的项目突然爆红❗一场由 ESLint 和格式化配置缺失引发的血案🧩
前端·visual studio code·cursor