Maison  >  Article  >  interface Web  >  Principe de collecte de dépendances du code source de Vue

Principe de collecte de dépendances du code source de Vue

不言
不言original
2018-07-09 11:14:451430parcourir

Cet article présente principalement le principe de collecte de dépendances du code source de Vue. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer

Vue est actuellement le troisième front national. end web end. L'un des meilleurs au monde, et c'est aussi l'une de mes principales piles technologiques. Je le connais dans mon utilisation quotidienne et je suis curieux de savoir pourquoi. De plus, un grand nombre d'articles sur la lecture du code source de Vue sont apparus. dans la communauté récemment. J'aimerais profiter de cette opportunité pour apprendre de tout le monde. J'ai acquis un peu de nutrition grâce aux articles et aux discussions, et en même temps, j'ai résumé quelques réflexions lors de la lecture du code source et j'ai produit quelques articles comme résumé des miens. réflexion. Mon niveau est limité, alors veuillez laisser un message pour discussion ~

Version Target Vue : 2.5.17-beta.0
Commentaires sur le code source de Vue : https://github.com/SHERlocked...
Avertissement : la syntaxe du code source dans l'article utilise Flow, et le code source est supprimé si nécessaire (afin de ne pas être confondu@_@), si vous souhaitez voir la version complète, veuillez saisir l'adresse github ci-dessus .Cet article est une série d'articles.Voir l'adresse de l'article en bas~

1. Système réactif

Nous savons que Vue grâce à l'introduction sur le site officiel est un framework MVVM. Il ne se soucie pas des changements de vue, mais pilote les mises à jour des vues via les données, ce qui rend notre gestion d'état très simple. Comment y parvenir ? Voler une image sur le site officiel

Principe de collecte de dépendances du code source de Vue

Chaque instance de composant a un objet d'instance Watcher correspondant, qui enregistrera les propriétés en tant que dépendances pendant le processus de rendu du composant, puis quand Lorsque le setter d'une dépendance est appelé, il demande au watcher de recalculer, provoquant la mise à jour de ses composants associés.

Il y a ici trois concepts importants Observe, Dep, Watcher, situés respectivement en src/core/observer/index.js, src/core/observer/dep.js, src/core/observer/watcher.js

  • Observe La classe est principalement utilisée pour ajouter les attributs de l'objet responsive getter/setter pour la collecte des dépendances et l'envoi des mises à jour

  • Dep La classe est utilisée pour collecter les dépendances du objet réactif actuel

  • Watcher La classe est un observateur et les instances sont divisées en trois types : observateur de rendu, observateur de propriétés calculées et observateur d'auditeur

2. Implémentation du code

2.1 initState

Le point d'entrée responsive est situé dans initState de src/core/instance/init.js :

// src/core/instance/state.js

export function initState(vm: Component) {
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)              // 初始化props
  if (opts.methods) initMethods(vm, opts.methods)        // 初始化methods
  if (opts.data) initData(vm)                            // 初始化data
  if (opts.computed) initComputed(vm, opts.computed)     // 初始化computed
  if (opts.watch) initWatch(vm, opts.watch)              // 初始化watch
  }
}

Il en définit plusieurs très régulièrement. Il existe plusieurs méthodes pour initialiser props, methods, data, computed, wathcer Jetons un coup d'œil à la méthode initData pour avoir un aperçu de <.>

// src/core/instance/state.js

function initData(vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
                    ? getData(data, vm)
                    : data || {}
  observe(data, true /* asRootData */)             // 给data做响应式处理
}
. Tout d'abord, nous jugeons si les données sont une fonction, si c'est le cas, la valeur de retour est prise. Sinon, la valeur de retour est prise. Ensuite, il y a une méthode

à traiter <.>. Cette méthode tente de créer une instance Observer observe Si elle est créée avec succès, elle renvoie une nouvelle instance Observer, renvoie l'instance Observer existante data__ob__2.2 Observer/defineReactive.

Cette méthode utilise principalement
// src/core/observer/index.js

export function observe (value: any, asRootData: ?boolean): Observer | void {
  let ob: Observer | void
  ob = new Observer(value)
  return ob
}
comme paramètre pour instancier une instance d'objet Observer. Observer est une classe, utilisée pour la collection de dépendances et la mise à jour

. méthode pour répondre aux clés de l'objet, ajoute récursivement data aux attributs de l'objet et déclenche Et lorsque la valeur est modifiée, déclenchez notify d'abord, puis déclenchez defineReactive. et envoyer des mises à jour getter/settergettergettersetter pour collecter les dépendances Notez ici, uniquement lorsque

La collecte des dépendances ne sera effectuée que lorsqu'il y a une valeur dans
// src/core/observer/index.js

export class Observer {
  value: any;
  dep: Dep;

  constructor (value: any) {
    value: any;
    this.dep = new Dep()
    def(value, '__ob__', this)    // def方法保证不可枚举
    this.walk(value)
  }

  // 遍历对象的每一个属性并将它们转换为getter/setter
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i  Lorsque la méthode <p> de. l'instance Watcher est appelée, <code>getter</code> poussera la valeur actuelle de l'observateur dans <code>Dep.target</code>, et l'observateur d'origine sera poussé sur la pile <code>Dep.target</code> Dans la pile, la valeur actuelle de l'observateur est extraite de la pile. et la valeur d'observateur d'origine est attribuée à <code>get</code> efface enfin les observateurs qui ne sont plus dans le nouveau <code>pushTarget</code> pour empêcher la modification de la vue lorsque l'observateur inutile se déclenche <code>Dep.target</code><code>targetStack</code>, d'abord <code>Dep.target</code> et retournez s'il n'y a pas de changement par rapport à l'ancienne valeur. Si un changement se produit, dep informe tous les Watchers stockés dans les sous-marins qui dépendent de cette instance <code>cleanupDeps</code> qui sera mise à jour. poussé de manière asynchrone vers la file d'attente des observateurs du planificateur <code>newDeps</code> Lors de nextTick, </p> retirera l'observateur de la file d'attente pour l'exécution <p> et exécutera les hooks associés <code>setter</code><code>getter</code>2.3 Dep<code>update</code><code>update</code>. Un mot-clé <code>queueWatcher( )</code> est mentionné à plusieurs reprises ci-dessus. Il s'agit d'un conteneur pour la collecte de dépendances, ou il est appelé <code>queue</code>collecteur de dépendances <code>flushSchedulerQueue( )</code> Il enregistre quels Watchers s'appuient sur leurs propres modifications, ou en d'autres termes, quels Watchers s'abonnent. à leurs propres changements ; voici une citation d'un internaute : <code>watcher.run</code></p><blockquote>@liuhongyi0101 :简单点说就是引用计数 ,谁借了我的钱,我就把那个人记下来,以后我的钱少了 我就通知他们说我没钱了</blockquote><p>而把借钱的人记下来的小本本就是这里 <code>Dep</code> 实例里的subs</p><pre class="brush:php;toolbar:false">// src/core/observer/dep.js

let uid = 0            // Dep实例的id,为了方便去重

export default class Dep {
  static target: ?Watcher           // 当前是谁在进行依赖的收集
  id: number
  subs: Array<watcher>              // 观察者集合
  
  constructor() {
    this.id = uid++                             // Dep实例的id,为了方便去重
    this.subs = []                              // 存储收集器中需要通知的Watcher
  }

  addSub(sub: Watcher) { ... }  /* 添加一个观察者对象 */
  removeSub(sub: Watcher) { ... }  /* 移除一个观察者对象 */
  depend() { ... }  /* 依赖收集,当存在Dep.target的时候把自己添加观察者的依赖中 */
  notify() { ... }  /* 通知所有订阅者 */
}

const targetStack = []           // watcher栈

export function pushTarget(_target: ?Watcher) { ... }  /* 将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中 */
export function popTarget() { ... }  /* 将观察者实例从target栈中取出并设置给Dep.target */</watcher>

这里 Dep 的实例中的 subs 搜集的依赖就是 watcher 了,它是 Watcher 的实例,将来用来通知更新

2.4 Watcher

// src/core/observer/watcher.js

/* 一个解析表达式,进行依赖收集的观察者,同时在表达式数据变更时触发回调函数。它被用于$watch api以及指令 */
export default class Watcher {
  constructor(
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean      // 是否是渲染watcher的标志位
  ) {
    this.getter = expOrFn                // 在get方法中执行
    if (this.computed) {                   // 是否是 计算属性
      this.value = undefined
      this.dep = new Dep()                 // 计算属性创建过程中并未求值
    } else {                               // 不是计算属性会立刻求值
      this.value = this.get()
    }
  }

  /* 获得getter的值并且重新进行依赖收集 */
  get() {
    pushTarget(this)                // 设置Dep.target = this
    let value
    value = this.getter.call(vm, vm)
    popTarget()                      // 将观察者实例从target栈中取出并设置给Dep.target
    this.cleanupDeps()
    return value
  }

  addDep(dep: Dep) { ... }  /* 添加一个依赖关系到Deps集合中 */
  cleanupDeps() { ... }  /* 清理newDeps里没有的无用watcher依赖 */
  update() { ... }  /* 调度者接口,当依赖发生改变的时候进行回调 */
  run() { ... }  /* 调度者工作接口,将被调度者回调 */
  getAndInvoke(cb: Function) { ... }
  evaluate() { ... }  /* 收集该watcher的所有deps依赖 */
  depend() { ... }  /* 收集该watcher的所有deps依赖,只有计算属性使用 */
  teardown() { ... }  /* 将自身从所有依赖收集订阅列表删除 */
}

get 方法中执行的 getter 就是在一开始new渲染watcher时传入的 updateComponent = () => { vm._update(vm._render(), hydrating) },这个方法首先 vm._render() 生成渲染VNode树,在这个过程中完成对当前Vue实例 vm 上的数据访问,触发相应一众响应式对象的 getter,然后 vm._update()patch

注意这里的 get 方法最后执行了 getAndInvoke,这个方法首先遍历watcher中存的 deps,移除 newDep 中已经没有的订阅,然后 depIds = newDepIds; deps = newDeps ,把 newDepIdsnewDeps 清空。每次添加完新的订阅后移除旧的已经不需要的订阅,这样在某些情况,比如 v-if 已不需要的模板依赖的数据发生变化时就不会通知watcher去 update

2.5 小结

整个收集的流程大约是这样的,可以对照着上面的流程看一下

Principe de collecte de dépendances du code source de Vue

watcher 有下面几种使用场景:

  • render watcher 渲染 watcher,渲染视图用的 watcher

  • computed watcher 计算属性 watcher,因为计算属性即依赖别人也被人依赖,因此也会持有一个 Dep 实例

  • watch watcher 侦听器 watcher

只要会被别的观察者 (watchers) 依赖,比如data、data的属性、计算属性、props,就会在闭包里生成一个 Dep 的实例 dep 并在被调用 getter 的时候 dep.depend 收集它被谁依赖了,并把被依赖的watcher存放到自己的subs中 this.subs.push(sub),以便在自身改变的时候通知 notify 存放在 dep.subs 数组中依赖自己的 watchers 自己改变了,请及时 update ~

只要依赖别的响应式化对象的对象,都会生成一个观察者 watcher ,用来统计这个 watcher 依赖了哪些响应式对象,在这个 watcher 求值前把当前 watcher 设置到全局 Dep.target,并在自己依赖的响应式对象发生改变的时候及时 update

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

jQuery源码之Callbacks的学习

浏览器与NodeJS的EventLoop异同以及部分机制

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn