Heim  >  Artikel  >  Web-Frontend  >  Prinzip der Abhängigkeitssammlung des Vue-Quellcodes

Prinzip der Abhängigkeitssammlung des Vue-Quellcodes

不言
不言Original
2018-07-09 11:14:451430Durchsuche

Dieser Artikel stellt hauptsächlich das Abhängigkeitssammlungsprinzip des Vue-Quellcodes vor. Er hat einen gewissen Referenzwert. Jetzt kann ich ihn mit allen teilen, die ihn benötigen.

Vue ist derzeit die dritte inländische Front. End-Web-End. Es ist auch einer meiner wichtigsten Technologie-Stacks. Ich kenne es im täglichen Gebrauch und bin gespannt, warum es eine große Anzahl von Artikeln zum Lesen von Vue-Quellcode gibt In letzter Zeit möchte ich diese Gelegenheit nutzen, um von allen zu lernen. Ich habe aus den Artikeln und Diskussionen etwas gewonnen, habe beim Lesen des Quellcodes einige Gedanken zusammengefasst und einige Artikel als Zusammenfassung meiner eigenen Gedanken erstellt . Mein Level ist begrenzt, also hinterlassen Sie bitte eine Nachricht zur Diskussion~

Target Vue-Version: 2.5.17-beta.0
Vue-Quellcode-Kommentare: https://github.com/SHERlocked...
Haftungsausschluss: Die Syntax des Quellcodes im Artikel verwendet Flow und der Quellcode wird nach Bedarf gelöscht (um nicht verwirrt zu werden@_@). Wenn Sie die Vollversion sehen möchten, geben Sie bitte oben die Github-Adresse ein. Bei diesem Artikel handelt es sich um eine Artikelserie unten kümmert sich nicht um Ansichtsänderungen, sondern steuert Ansichtsaktualisierungen über Daten, was unsere Zustandsverwaltung sehr einfach macht. Ein Bild von der offiziellen Website stehlen

Jede Komponenteninstanz verfügt über ein entsprechendes

Instanzobjekt, das die Eigenschaften während des Komponentenrenderingprozesses und dann als Abhängigkeiten aufzeichnet Wenn das Prinzip der Abhängigkeitssammlung des Vue-Quellcodes einer Abhängigkeit aufgerufen wird, benachrichtigt es das

zur Neuberechnung, wodurch die zugehörigen Komponenten aktualisiert werden.

WatcherHier gibt es drei wichtige Konzepte setter, watcher,

, die sich jeweils in

, Observe, DepWatchersrc/core/observer/index.jssrc/core/observer/dep.jssrc/core/observer/watcher.js befinden

Die Klasse wird hauptsächlich zum Hinzufügen der Attribute des reaktionsfähigen Objekts
    für die Sammlung von Abhängigkeiten und zum Versenden von Aktualisierungen verwendet.
  • Observegetter/setter

    Die Klasse wird zum Sammeln der Abhängigkeiten des Objekts verwendet aktuelles reaktionsfähiges Objekt
  • Dep

    Die Klasse ist ein Beobachter, und die Instanzen sind in drei Typen unterteilt: Rendering-Watcher, berechnete Eigenschafts-Watcher und Listener-Watcher
  • Watcher2. Code-Implementierung

  • 2.1 initState

Der responsive Eingang befindet sich in

von 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
  }
}

Es definiert sehr regelmäßig mehrere Methoden. Um

, initState,

,

, props zu initialisieren, werfen wir einen Blick auf die Methode methods, um einen Blick auf data

// 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做响应式处理
}
computed zu werfen Bestimmt zunächst, ob es sich bei den Daten um eine Funktion handelt. Wenn nicht, wird eine wathcer-Methode verwendet. Diese Methode versucht, eine Observer-Instanz zu erstellen Wenn es erfolgreich erstellt wurde, wird eine neue Observer-Instanz zurückgegeben. Die Instanz gibt die vorhandene Observer-Instanz zurück. initData2.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
}
observeDiese Methode verwendet hauptsächlich data as Ein Parameter zum Instanziieren einer Observer-Objektinstanz. Observer ist eine Klasse für die Sammlung und Aktualisierung von Abhängigkeiten. Der Konstruktor von Observer verwendet die Methode __ob__, um auf die Schlüssel des Objekts zu reagieren, und fügt rekursiv zu den Attributen des Objekts hinzu

, wenn die Daten erfasst werden, und Abhängigkeiten werden erfasst, wenn der Wert geändert wird Beachten Sie hier, dass es nur dann ausgelöst wird, wenn in

ein Wert vorhanden ist. Bei der Abhängigkeitssammlung wird dieser

aufgerufen, wenn die data-Methode der Watcher-Instanz aufgerufen wird des Beobachters in notify und schieben Sie den ursprünglichen Beobachter auf den Stapel defineReactive. Nachdem der Beobachter des Werts übernommen wurde, nehmen Sie ihn vom Stapel und weisen Sie den ursprünglichen Beobachterwert getter/setter zu, getter und löschen Sie schließlich den Beobachter, die im neuen getter nicht mehr vorhanden sind, um zu verhindern, dass nutzlose Beobachter, die in der Ansicht nicht mehr benötigt werden, ausgelöst werdensetter

zuerst getter und zurückkehren, wenn sich im Vergleich zum alten keine Änderung ergibt Wenn eine Änderung auftritt, benachrichtigt dep alle Watcher-Instanzen Dep.target, die zur Aktualisierung auf diese Daten angewiesen sind. Hier wird Dep.target bei nextTick nimmt den Watcher in der Warteschlange heraus und führt get aus und führt die entsprechende Hook-Funktion aus pushTargetDep.target2.3 DeptargetStackDep.targetEin Schlüsselwort cleanupDeps wird oben oft erwähnt Es ist ein Container für Abhängigkeitssammlung, oder sie heißt newDepsAbhängigkeitssammler

. Sie zeichnet die Änderungen auf, die von den Beobachtern selbst abhängen, oder sagen wir, welche Beobachter ihre eigenen Änderungen abonniert haben:

@liuhongyi0101 :简单点说就是引用计数 ,谁借了我的钱,我就把那个人记下来,以后我的钱少了 我就通知他们说我没钱了

而把借钱的人记下来的小本本就是这里 Dep 实例里的subs

// 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 小结

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

Prinzip der Abhängigkeitssammlung des Vue-Quellcodes

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异同以及部分机制

Das obige ist der detaillierte Inhalt vonPrinzip der Abhängigkeitssammlung des Vue-Quellcodes. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn