This is the first day of my participation in the August Text Challenge.More challenges in August

A clock, under normal circumstances, we only know that the second hand should move once a second, so why does it move once a second, this requires us to understand the structure of the clock, so that it can be repaired when it goes wrong. Vue is the same, usually know how to use the development line, but suddenly appeared a inconsistent with the expected results, master some source knowledge can help you quickly locate the problem; On the other hand, interviews tend to be biased towards source code, and if you don’t, then you’re behind others. To do this, I also had to take the time to learn the source code to deal with the inner volume.

Source code analysis

  • The listenerObserverIntercepts all properties and listens for property changes
// core/instance/state.js
function initData(){...// Whether data is an object
    // Attribute weight and other operations
   observe(data, true /* asRootData */) // Listen for data
}
Copy the code
// core/observer/index.js
export function observe(value, asRootData){
    let ob
    ob = new Observer(value) // Create a listener instance
    return ob
}
Copy the code
// core/observer/index.js
export class Observer{
    constructor(value) {
        if (Array.isArray(value)) { // Is an array type
            / / rewrite array methods' push ', 'pop', 'shift' and 'unshift', 'splice', 'sort of' reverse '
            // ...
        } else {
            this.walk(value)
        }
    }
    walk(obj) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]) // Iterate over attributes to add interception}}}Copy the code
// core/observer/index.js
export function defineReactive(){
    const dep = new Dep()   // Each attribute corresponds to a Dep,
    // ...! shallow && observe(val)// There may be nested objects in data, continue recursion
     Object.defineProperty(obj, key,{
         get(){
             dep.depend()  // Triggered when watcher access is collected in get
         },
         set(){ childOb = ! shallow && observe(newVal)// Set assigns an object and continues to recursively intercept the object
             dep.notify() // Publish subscription messages}})}Copy the code
  • A single piece of data can be used in more than one place on a page, so one attribute can correspond to more than oneWatcher, set aDepTo manage theWatcher
// core/observer/dep.js
class Dep(a){
    depend(){
        if (Dep.target) { //Dep.target is a watcher
            Dep.target.addDep(this)
        }
    }
    addSub (sub: Watcher) {
        this.subs.push(sub) // One Dep corresponds to multiple Watcher
    }
    notify () {
        for (let i = 0, l = subs.length; i < l; i++) { // Walk through the collected Watcher,
            subs[i].update()// Update Watcher in sequence}}}Copy the code
  • The subscriberWatcherReceive property changes to update the corresponding view
// core/observer/watcher.js
class Watcher(a){
    addDep(dep){
        dep.addSub(this) // Add watcher to dep.subs, which holds the watcher list
    }
    update() {
    // Synchronous or asynchronous update?
     this.run()
    }
    run(){
        const  value = this.get() // Get the current value
        if( value ! = =this.value ||
            isObject(value) ||
            this.deep
        ){ // The old value is not equal to the new value, the value is an object, deep mode
            this.cb.call(this.vm, value, oldValue)  // Execute Watcher's callback function}}get(){
        value = this.getter.call(vm, vm) // Refire the property's get method to collect the watcher
        this.cleanupDeps()
        return value
    }
    cleanupDeps() {
        // Run through all dePs and remove the subscription to Watcher from each Dep
        let i = this.deps.length
        while (i--) {
            const dep = this.deps[i]
            if (!this.newDepIds.has(dep.id)) {
                // Remove the obsolete Watcher based on the subscribed ID to avoid the additional cost of updating
                dep.removeSub(this)}}// assign newDeps,newDepIds to depIds, deps, and empty
        let tmp = this.depIds
        this.depIds = this.newDepIds
        this.newDepIds = tmp
        this.newDepIds.clear()
        tmp = this.deps
        this.deps = this.newDeps
        this.newDeps = tmp
        this.newDeps.length = 0}}Copy the code

The specific process

Depend on the collection

The get () = = = > dep. Depend () = = = > (dep. The target is the Watcher) dep. Target. AddDep () = = = > dep. AddSub ()

  • Property is triggered when the property is readgetInterceptor operations
  • Into theDepObject, one for each propertyDep, the use ofdep.depend()Method to collect related dependencies
  • Into thewatcher.addDepMethod,WatcherObject has onenewDepsandnewDepIdsProperty, which is stored with thisWatcherThe relevantDepObject, oneWatcherCorresponding to multipleDep.newDepsWhat’s stored inside isDepThe list of
  • Back to theDepObject, place thisWatcherAdded to thesubsProperty, oneDepCorresponding to multipleWatcher
graph LR;
  prop1-->dep1-->watcher1
  prop2-->dep2-->watcher2 & watcher1 
  prop3-->dep3-->watcher3 & watcher2 
  prop4-->dep4-->watcher4
 

Distributed update

set()--->dep.notify()--->watcher.update()--->watcher.run()--->watchrt.get()--->this.getter.call(vm, vm)--->watcher.cleanupDeps()---> this.cb.call(this.vm, value, oldValue)

  • Triggered when assigning a value to a propertyset()Interceptor operations
  • Go to the corresponding of this propertyDepObject, traversalsubsProperty to store inwatcherList, ready for update
  • When an update is made, it determines whether the update is synchronous or asynchronous, and then performs different operations in a more updated manner
  • Sync updates intorun()Method, triggerget()Method to get the updated value
  • Enter theget()The method is called againhis.getter.call(vm, vm)The triggergetterInterceptor, gets the value of the property, and recollects the dependency
  • Triggered after the dependency is recollectedclearupDeps()Clean up ‘Watcher’ that is not relevant to the current update
  • performwatcherThe callback function in

The articles

  • [Vue source]–$nextTick and asynchronous rendering (line by line comments)
  • [Vue source]–mixin and extend (line by line)
  • [Vue source]–Diff algorithm (line by line comment)
  • [Vue source]– How to listen for array changes (line by line comment)
  • [Vue source]– Reactive (bidirectional binding) principle (line by line comment)
  • [Vue source]– what is done for each lifecycle (line by line comments)
  • [Vue source]– How to generate virtual DOM (line by line comment)