Vue.js 核心系列 - 响应式原理

想对 Vue 有一个较深的了解,就必须要深入内核,看清楚当我们数据发生变化或触发动作时,Vue 是如何帮助我们更新视图的。

首先让我们来看一下“栗子”:

  • 模板:

    1
    2
    3
    <div id="app" @click="changeMsg">
    {{ message }}
    </div>
  • 脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var app = new Vue({
    el: '#app',
    data: {
    message: 'Hello Vue!'
    },
    methods: {
    changeMsg() {
    this.message = 'Hello World!'
    }
    }
    })

这里我们可以看到,当我们触发 changeMsg 事件时,模板中的 message 会被更新,并且我们的视图也会随之更新,那么这里发生了什么呢?

我们来看一下下面这张图:

响应式原理(来自:Vue.js 技术揭秘)

下面分两个部分去了解其中的“玄机”:

几个核心概念

  • 监听器 Observer ,用来劫持并监听所有属性(转变成setter/getter形式),如果属性发生变化,就通知订阅者
  • 订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理
  • 订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图
  • 解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化

核心流程

  • vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
  • 在初始化时调用 defineReactive,实例化一个 dep 负责派发更新,使用 Object.defineProperty 中的 setter 负责依赖收集
  • Dep:负责管理 Watcher,dep.notify() 实际调用 watcher 中的 update,按照需要更新的队列依次更新(按 id 先后顺序)
  • 调用 watcher.before() 触发 beforeUpdate 的 hook,最终通过 vm._update 更新视图(到 diff 流程中)
    • 实现一个数据监听器 Observer,对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
    • (实现一个指令解析器 Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数)
    • 实现一个 Watcher,作为连接Observer和Compile的桥梁,订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

参考

Vue.js 核心揭秘 - 深入响应式原理

如果觉得有用,您的支持将鼓励我继续创作!