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~
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
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
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做响应式处理 }
à 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.
// src/core/observer/index.js export function observe (value: any, asRootData: ?boolean): Observer | void { let ob: Observer | void ob = new Observer(value) return ob }
. 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/setter
getter
getter
setter
pour collecter les dépendances Notez ici, uniquement lorsque
// 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
的实例,将来用来通知更新
// 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
,把 newDepIds
和 newDeps
清空。每次添加完新的订阅后移除旧的已经不需要的订阅,这样在某些情况,比如 v-if
已不需要的模板依赖的数据发生变化时就不会通知watcher去 update
了
整个收集的流程大约是这样的,可以对照着上面的流程看一下
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中文网!
相关推荐:
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!