资讯专栏INFORMATION COLUMN

【前端进阶基础】VUE响应式数据原理 订阅-发布模式解析

陈伟 / 1249人阅读

摘要:其成员函数最主要的是和,前者用来设置某个的依赖,后者则用来通知与这个依赖相关的来运行其回调函数。也就是进行数据更新操作。

vue框架的两个抽象核心:虚拟DOM和相应式数据原理

关于虚拟DOM的核心算法,我们上一章已经基本解析过了,详细的见
React && VUE Virtual Dom的Diff算法统一之路 snabbdom.js解读

关于响应式数据原理,我们先看张图
你 ‘ (4).png

具体来讲,要分以下几步:

初始化实例对象时运行initState, 建立好props, data 的钩子以及其对象成员的Observer, 对于computed 属性,则建立起所有对应的 Watcher 并且通过 Object.defineProperty 在vm对象上设置一个该属性的 getter。同时还根据自定义的 watch 来建立相应的 Watcher

执行挂载操作,在挂载时建立一个直接对应render(渲染)的 Watcher,并且编译模板生成 render 函数,执行vm._update 来更新 DOM 。

此后每当有数据改变,都将通知相应的 Watcher 执行回调函数。

Observer 劫持者
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, "__ob__", this)
    if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through each property and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

首先我们观察到,new Observer()的时候,会进行类型的判断

if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }

如果是数组类型则会递归调用,建立依赖体系
否则则调用walk()函数去利用Object.defineProperty简历依赖

那么defineReactive是如何实现的呢,如下

//省略了部分代码
export function defineReactive (
 obj: Object,
 key: string,
 val: any,
 customSetter?: ?Function,
 shallow?: boolean
) {
  const dep = new Dep()
  ...
  Object.defineProperty(obj, key, {
      ...
      get: function reactiveGetter () {
        ...
        dep.depend()
        ...
        return value
      },
      set: function reactiveSetter (newVal) {
        ...
        dep.notify()
      }
    })
}

拦截器会分别在getter和setter上设置中间件去维护dep数组中的依赖关系

dep.depend()

dep.notify()

字面意思非常明显,分别用于增加依赖和通知依赖变化关系

Dep (dependency // 依赖)
class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

Dep 就是一个 Watcher 所对应的数据依赖,在这个对象中也存有一个 subs 数组,用来保存和这个依赖有关的 Watcher。其成员函数最主要的是 depend 和 notify ,前者用来设置某个 Watcher 的依赖,后者则用来通知与这个依赖相关的 Watcher 来运行其回调函数。

我们知道,Dom 上通过指令或者双大括号绑定的数据,会为数据进行添加观察者watcher,当实例化Watcher的时候 会触发属性的getter方法,此时会调用dep.depend()。我们看一下depend方法:

depend () {
  if (Dep.target) {
    Dep.target.addDep(this)
  }
}

Dep.target 是什么东西呢?其实在进行Watcher实例化的时候,会调用内部的get函数,开始为他初始化

Watcher 观察者

其中pushTarget 方法就是为Dep.target绑定此watcher实例,所以Dep.target.addDep(this)也就是执行此实例中的addDep方法

addDep (dep: Dep) {
  ...
  dep.addSub(this)
}

这样便为我们的dep实例添加了一个watcher实例。

接着当我们更新data的时候,会触发他的set方法,执行dep.notify()函数:

notify () {
  // stabilize the subscriber list first
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
    subs[i].update()
  }
}

这里也就是遍历dep中收集到的watcher实例,进行update()。也就是进行数据更新操作。这也就是简单的数据响应式,其实还需要注意的是: 当数据的getter触发后,会收集依赖,但也不是所有的触发方式都会收集依赖,只有通过watcher触发的getter会收集依赖:if (Dep.target) { dep.depend() },而所谓的被收集的依赖就是当前watcher,DOM中的数据必须通过watcher来绑定,只通过watcher来读取。

最后付一个函数timeline
的.png

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102995.html

相关文章

  • 源码解析 —— Vue响应数据

    摘要:下面我们会向大家解释清楚为什么这个这么重要,以及它和的响应式数据流有什么关系。源码前面铺垫这么多就是希望大家能理解接下来要讲的响应式数据流。总结讲到这里大家应该都能够明白的响应式数据流是如何实现的。 Vue、React介绍 目前前端社区比较推崇的框架有Vue 和 React,公司内部许多端都自发的将原有的老技术方案(widget + jQuery)迁移到 Vue / React上了。我...

    LuDongWei 评论0 收藏0
  • vue.js响应原理解析与实现

    摘要:今天,就我们就来一步步解析响应式的原理,并且来实现一个简单的。当然,这个也只是一个简单的,来说明响应式的原理,真实的源码会更加复杂,因为加了很多其他逻辑。接下来我可能会将其与联系起来,实现和语法。 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染。之后,再接触了vue.js,当时也一度很好奇vue.js是如何监...

    Shihira 评论0 收藏0
  • 深入浅出Vue响应原理

    摘要:总结最后我们依照下图参考深入浅出,再来回顾下整个过程在后,会调用函数进行初始化,也就是过程,在这个过程通过转换成了的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行函数,而在当被赋值的时候会执行函数。 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解...

    yiliang 评论0 收藏0
  • Vue 进阶系列(一)之响应原理及实现

    摘要:进阶系列一之响应式原理及实现进阶系列二之插件原理及实现进阶系列三之函数原理及实现什么是响应式表示一个状态改变之后,如何动态改变整个系统,在实际项目应用场景中即数据如何动态改变。描述符必须是这两种形式之一,但二者不能共存,不然会出现异常。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导)showImg(https://githu...

    MonoLog 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<