navigation

[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise

[react] Hooks

[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI

[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend

Vue. NextTick and VM.$nextTick

Front knowledge

Some words

The teardown was a somewhat expensive operationCopy the code

Use case

<template>
  <div class="about">
    <h1>This is a watch page</h1>

    <div>count = {{count}}</div>
    <button @click="changeCount"> count</button> <br /> <div>immediate execute: count1 = {{count1}}</div> < button@click ="changeCount1"< span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important; white-space: inherit! Important;" count2 = {{nestObj.count2}}</div> <button @click="changeCount2">change count2</button>
    <br />
    <br />

    <div>count3 = {{nestObj.count3}}</div>
    <button @click="changeCount3">change count3</button>
    <br />
    <br />

    <button @click="changeTestArr">change testArr</button>
    <br />
    <br />

    <button @click="changeAll"</button> </div> </template> <script>export default {
  data() {
    return {
      count: 0,
      count1: 1,
      nestObj: {
        count2: 2,
        count3: 3
      },
      testArr: {
        count4: 4,
        count5: 5
      },
      testHandlerIsFunctionName: 6,
    };
  },
  computed: {
    deepCopyNestObj() {
      return JSON.parse(JSON.stringify(this.nestObj))
    }
  },
  watch: {
    count: function(val, newVal) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the console () function. The log (val, newVal); }, count1: { handler(v, oldv) { console.log(v, oldv,"Immediate execution does not depend on changes."."Post execution");
      },
      immediate: true}, nestObj: {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- object handler (v, oldv) {the console. The log (v.c ount2, oldv. Count2,"Sync before nextTick");
      },
      deep: true,
      sync: true}, deepCopyNestObj(newVal, oldVal) {console.log(newval.count2, oldval.count2,'Deep depth observation - the problem is that the old and new values are the same, you can make a deep copy with Commputed')},"nestObj.count3": function() {// listen for a property in the object. You can use the string of obj.xxx as the key console.log()."Watch the nestObj. Count3");
    },
    testArr: / / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the arrayfunction handler1() {
        console.log(1111);
      },
      function handler2() { console.log(2222); }].testHandlerIsFunctionName: 'watchHandlerIsFnName'// --------------- string // watchHandlerIsFnName is a method defined in the methods method // whentest}, methods: {watchHandlerIsFnName(v, oldV) {console.log(v, oldV,'The handler for the Watch object is a string, which is a method name.')},changeCount() {
      this.count = this.count + 1;
    },
    changeCount1() {
      this.count1 = this.count1 + 1;
    },
    changeCount2() {
      this.nestObj.count2 = this.nestObj.count2 + 1;
    },
    changeCount3() {
      this.nestObj.count3 = this.nestObj.count3 + 1;
    },
    changeAll() {
      this.count = this.count + 1;
      this.count1 = this.count1 + 1;
      this.nestObj.count2 = this.nestObj.count2 + 1;
      this.nestObj.count3 = this.nestObj.count3 + 1;
      this.testArr = this.testArr + 1;
      this.testHandlerIsFunctionName = this.testHandlerIsFunctionName + 1
    },
    changeTestArr() {
      this.testArr = this.testArr + 1
    }
  }
};
</script>
Copy the code

Learning goals

  • Two ways to use watch

    • The watch is treated as an object by the component’s argument
    • This is called through the vm.$watch() method
  • Avoid infinite loops

    • Watch: {count: {this.count = this.count + 1}}
    • It’s going to watch the count change, change the count, change the count, and then call CB to change the count, so it’s going to loop
  • The type of value for the key object of the wath object

    • function
    • object
    • array
    • string
    • The end result is to convert different types of handlers into functions
  • Properties supported by the Options object of the Watch object

    • deep
      • The depth of the listening
      • Loop through (access) each property of the VM.key nested object corresponding to the key in the watch object, triggering a data-dependent reactive get via dep.depend()
        • Add deP to User Watcher’s newDeps
        • Add user watcher to the SUBs of the DEP
    • immediate
      • Execute CB, the handler function in the wache object, immediately, without waiting for the dependency to change
      • Call cb(watcher.value) directly
    • sync
      • Ensure that (the synchronized Wath object handler) is executed before (the normal Watch object handler)
      • Sync directly calls watcher.run() => this.cb.call(this.vm, value, oldValue) to execute the cb function directly
  • Watch initialization process

    1. Handle all types of values corresponding to the Watche object key, and process object,array,string as object functions
    2. Implement vm. $watchg
    3. new userWatcher()
      • Constructor by enclosing the get () call the getter function, the key watch object by enclosing the getter = parsePath divided into array (expOrFn) method, through the vm [key] to visit, Returns the reactive data for the key in the Watch object
      • When accessed, the get method of reactive data is triggered to collect dependencies, and user watcher is collected in DEP for update
  • Update process

    • Depending on the change, dep.notify() is triggered, and watcher.update() in the magic dep.subs data is updated
      • Watcher.run => this.cb.call(this.vm, value, oldValue)
      • If sync=false, queueWatcher => nextTick(flushSchedulerQueue) => watcher.run() => this.cb.call(this.vm, value, oldValue)

Watch the source code

  • Vue.prototype._init => initState => initWatch(vm, opts.watch) => createWatcher(vm, key, handler) => vm.$watch(expOrFn, handler, options)

  • initWatch – src/core/instance/state.js

function initWatch (vm: Component, watch: Object) {
  // initWatch(vm, opts.watch)
  
  for (const key inWatch) {const handler = watch[key] // const handler = watch[key]if(array.isarray (handler)) {// Handler is an Array, pass each member to createWatcher // members are usually functions // such as // watch: {//testArr: [
        //     function handler1() { // console.log(1111); / / / /}function handler2() { // console.log(2222); //} //for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else{// handler is an object, function, string // for example // watch: {// count:function(val, newVal) {
      //     console.log(val, newVal);
      //   },
      //   count1: {
      //     handler(v, oldv) {
      //       console.log(v, oldv, "Immediate execution does not depend on changes."."Post execution");
      //     },
      //     immediate: true,
      //     deep: true,
      //     sync: true/ /, / /}testHandlerIsFunctionName: 'watchHandlerIsFnName'
      // }
      createWatcher(vm, key, handler)
    }
  }
}
Copy the code
  • createWatcher – src/core/instance/state.js
functionCreateWatcher (vm: Component, expOrFn: string | Function, / / watch the key object handler: Any, // watch object key corresponding to the value => object, function, array member, string options? : Object // initialized with undefined) {if(isPlainObject(handler)) {// Handler is an object // for example // count1: {// handler(v, oldv) {// console.log(v, oldv); // }, // immediate:true, / / deep:true,
        //   sync: true//} options = handler handler = handler.handler // If handler is an object, assign handler to options. }}}}}}}}}}}}if (typeof handler === 'string') {handler = vm[handler] // Handler is a string, assign the string to represent the method defined in the Methods object}return vm.$watchExpOrFn, handler, options // Import VM.$watch};};};}Copy the code
  • Vue.prototype.$watch – src/core/instance/state.js
  Vue.prototype.$watch = function(expOrFn: string | Function, / / expOrFn / / watch the object's key cb: Any, // cb // cb is the handler function converted from the watcher object. (It can be a function, array, object, string.) It goes directly through the VM.$watchIf passed, then cb could also be (function, object, array, string) options? // Handler immediate deep sync, etc.): Function {const VM: Component = thisif(isPlainObject(cb)) { because$watchThis can be done through the VM.$watch// 2. If you pass new Vue({}) to the watch object,cdIt's a function that's already been processed, so you don't have to judge the objectreturn}} createWatcher(VM, expOrFn, cb, options)}} } as a function of processing options = options | | {} options. The user =true// Add the user attribute to the options object with a value oftrueConst watcher = new audit watcher (vm, expOrFn, cb, options) // new 1 user watcherif(options. Immediate) {// run the cb handler function immediately if the immediate attribute exists. Try {cb.call(vm, watcher.value) // run the cb handler function immediately. // newValue // oldValue // watch: {// count:function(val, newVal) {
            //     console.log(val, newVal);
            //   }
            // }
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)}}return function unwatchFn () {
      //  Vue.prototype.$watchFunction, which returns the unwatchFn function watcher.tearDown () // watcher.tearDown () // 1. Delete user watcher // 2 from _watchers. Delete dep // from deps in user watcherteardown () {
      //   if (this.active) {
      //     // this.active = trueThe default istrue
      //     if(! _isBeingDestroyed) {// remove(this.vm._watchers, this) // // remove watcher from the Watcher array //} //let i = this.deps.length
      //     while(I --) {// this.deps[I].removesub (this) // // remove all watcher in the deps of watcher // //$watchDep //} // this.active =false
      //     // this.active = false//} //Copy the code
  • watcher – scr/core/observer/watcher.js

exportdefault class Watcher { vm: Component; expression: string; cb: Function; // For example, in user watcher, the handler function id: number; deep: boolean; user: boolean; lazy: boolean; // Mark of computed Watcher sync: Boolean; // User watcher options in the sync property: Boolean; // Used for computed Watcher Active: Boolean; deps: Array<Dep>; newDeps: Array<Dep>; depIds: SimpleSet; newDepIds: SimpleSet; before: ? Function; getter: Function; value: any; constructor ( vm: Component, expOrFn: string | Function, cb: Function, options? :? Object, isRenderWatcher? : boolean ) { this.vm = vmif (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    // options
    if(options) { this.deep = !! options.deep this.user = !! Options. user // user The default value of options.user for watcher istruethis.lazy = !! Options. lazy // For computed Watcher, the default options.lazy istruethis.sync = !! Before = options.before}else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.lazy // forlazy watchers this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() this.expression = process.env.NODE_ENV ! = ='production'
      ? expOrFn.toString()
      : ' '
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else}}}}}} {// expOrFn is not a function given or given by the audit object // Line operation using parsePath function enclosing getter = parsePath (expOrFn) / / this. Getter / / 1. ParsePath (expOrFn) / / return a function that returns the function parameter is a vm instance / /return function(obj) {// // 1.path => expOrFn = path ='a.b'/ / / / 2. Ojb = > / / / vm / 1 and 2 above, the following cycle: / / / / vm a = > access to the a / / / / vm. A. = > access to the / / bfor (let i = 0; i < segments.length; i++) {
            //     if(! obj)return
            //     obj = obj[segments[i]]
            //   }
            //   returnGetter.getter.call (vm, vm) // So: 1 returns function argument vm //export function parsePath (path: string): any {
      //   if (bailRE.test(path)) {
      //     return
      //   }
      //   const segments = path.split('. ') // // segments Possible case // // 1.'a.b'That is, observe attribute B of object A => [a, b] // // 2. A => [a] //return function(obj) {// // 1.path => expOrFn = path ='a.b'/ / / / 2. Ojb = > / / / vm / 1 and 2 above, the following cycle: / / / / vm a = > access to the a / / / / vm. A. = > access to the / / bfor (let i = 0; i < segments.length; i++) {
      //       if(! obj)return
      //       obj = obj[segments[i]]
      //     }
      //     returnObj // // returns the value returned by the responder get function //} //}if(! this.getter) { this.getter = noop process.env.NODE_ENV ! = ='production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }

  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)}else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

  /**
   * Add a dependency to this directive.
   */
  addDep (dep: Dep) {
    const id = dep.id
    if(! this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep)if(! this.depIds.has(id)) { dep.addSub(this) } } } /** * Clean upfor dependency collection.
   */
  cleanupDeps () {
    let i = this.deps.length
    while (i--) {
      const dep = this.deps[i]
      if(! this.newDepIds.has(dep.id)) { dep.removeSub(this) } }lettmp = 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 } /** * Subscriber interface. * Will be called when a dependency  changes. */update () {
    /* istanbul ignore else* /if (this.lazy) {
      this.dirty = true
    } else if(this.sync) {// For example, user watcher is configured with sync in options:trueCall the run method this.run()}else {
      queueWatcher(this)
      // export function queueWatcher (watcher: Watcher) {
      //   const id = watcher.id
      //   if (has[id] == null) {
      //     has[id] = true
      //     if(! flushing) { // queue.push(watcher) // }else{/ / / /if already flushing, splice the watcher based on its id
      //       // if already past its id, it will be run next immediately.
      //       let i = queue.length - 1
      //       while (i > index && queue[i].id > watcher.id) {
      //         i--
      //       }
      //       queue.splice(i + 1, 0, watcher)
      //     }
      //     // queue the flush
      //     if(! waiting) { // waiting =true
      
      //       if(process.env.NODE_ENV ! = ='production' && !config.async) {
      //         flushSchedulerQueue()
      //         return
      //       }
      //       nextTick(flushSchedulerQueue)
      //     }
      //   }
      // }
    }
  }

  /**
   * Scheduler job interface.
   * Will be called by the scheduler.
   */
  run () {
    if (this.active) {
      const value = this.get()
      if( value ! == this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { //set new value
        const oldValue = this.value
        this.value = value
        if} catch (e) {handleError(e, this.vm, value, oldValue); `callbackfor watcher "${this.expression}"`)}}else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }

  /**
   * Evaluate the value of the watcher.
   * This only gets called for lazy watchers.
   */
  evaluate () {
    this.value = this.get()
    this.dirty = false
  }

  /**
   * Depend on all deps collected by this watcher.
   */
  depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }

  /**
   * Remove self from all dependencies*/ tearDown () {if (this.active) {// this.active = true default true // remove self from VM 's watcher list
      // this is a somewhat expensive operation so we skip it
      // if the vm is being destroyed.
      if(! _isBeingDestroyed) {remove(this.vm._watchers, this) // Remove watcher from the watcher array}let i = this.deps.length
      while(I --) {this.deps[I].removesub (this);$watch(dep} this.active =); (dep} this.active =)false
      // this.active = false}}}Copy the code
  • parsePath – src/core/util/lang.js
export function parsePath (path: string): any {
  if (bailRE.test(path)) {
    return
  }
  const segments = path.split('. ') // segments possible // 1.'a.b'=> [a, b] // 2. A => [a]return function[url = https://exporfn} (url = https://exporfn) {// 1 // 1'a.b'A => access a // vm.a.b => access b // 2 // 1. Path => For example, expOrFn = path ='a'
      // 2. ojb => vm
    for (let i = 0; i < segments.length; i++) {
      if(! obj)return
      obj = obj[segments[i]]
    }
    returnObj // 1. Returns vm.a.b // 2. Returns vm.a}}Copy the code