浅析Vue响应式

2020-06-23  本文已影响0人  LeonLi_9ea5

Vue 响应式是什么

Vue 是一个 MVVM 的框架,即 Model-View-ViewModel,Model 与 View 之间不直接联系,而是由 ViewModel(相当于 Pipe) 去监听 Model 的变化并触发 View 改变以及监听 View 中的事件操作响应的 Model

我们来看一段简单的双向绑定的例子:

<template>
  <div id="app">
    <input
      type="text"
      v-model="msg"
      placeholder="edit me"
    >
    <h1 @click="rotate()">{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome'
    }
  },
  methods: {
    rotate () {
      this.msg = this.msg.split('').reverse().join('')
    }
  }
}
</script>

在这个例子中,<input>和<h1>均为 view,msg 为 model,而 Vue 则是我们的 ViewModel。
Vue 提供了两个工具:

Vue 如何实现响应式

先来了解几个名词

  1. Observer 观察者(监听器),每个可监听对象都会挂载一个观察者实例,负责订阅数据变化,通知对应的 Dep 实例

  2. Dep 消息订阅器(依赖收集器),负责依赖收集,管理 watcher,依赖收集操作在获取数据时执行(defineReactive > get)

  3. Watcher 订阅者,负责响应,Vue中有三种

    • User Watcher 组件的 watch 中定义的 watcher

      于 initWatch > createWatcher 中初始化

    • Computed Watcher 组件的 computed 中定义的 watcher

      于 initComputed 中初始化

    • Render Watcher 渲染 Watcher,只要有数据变化,最终都会由 Render Watcher 触发页面更新

      于 mountComponent 方法中 beforeMount 与 mounted 钩子之间初始化

再来看一下响应的流程

来自官网的盗图

reactive.png

稍微加工了一下,看下图


my-reactive.png

实线部分为内部实现;虚线部分为响应的流程。

结合上图,简单来说就是在 DOM 上操作数据(如 input )时,会被 Observer 中定义的 setter 函数劫持并调用 notify 函数通知消息订阅器 Dep,Dep 遍历其 subs 数组对所有的订阅者 Watcher 调用 update 函数,update 函数通过一系列操作更新 DOM

这一系列操作包括:

  1. queueWatcher 将当前 Watcher 放入待更新的 Watcher 队列中
  2. flushSchedulerQueue 依次执行队列中 Watcher 的 run 函数
  3. run 函数中const value = this.get()调用 Watcher 的 get 函数
  4. get 函数中执行value = this.getter.call(vm, vm)调用 Watcher 中定义的 expression
    • Render Watcher 中的 expression 是 function () { vm._update(vm._render(), hydrating); }
    • User Watcher 中的 expression 是用户自定义的 computed 中的函数
    • Computed Watcher 中的 expression 是用户自定义的 watch 中回调函数
  5. 执行vm._update(vm._render(), hydrating),vm._render() 返回一个新的 VNode,vm._update 中执行 vm.__patch__(prevVnode, vnode) 将VNode 渲染成真实 DOM 反应在页面上

接下来分别看这三者的实现

DefineReactive

/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()
  ...
  const getter = property && property.get
  const setter = property && property.set
  ...
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        ...
        dep.depend()
        ...
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      ...
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      ...
      dep.notify()
    }
  })
}

defineReactive 做了一下几件事

注意: 这里的 Dep.target 表示当前正在计算的 Watcher,其具有全局唯一性。

一些小知识

上一篇 下一篇

猜你喜欢

热点阅读