This article code and source code differences, forgive me!Copy the code

initWatch

Function initWatch (vm, watch) {// For (const key in watch) {const handler = watch[key]; If (array.isarray (handler)) {// If (array.isarray (handler)) {for (let I = 0; i < handler.length; i++) { createWatcher(vm, key, handler[i]) } } else { createWatcher(vm, key, handler) } } }Copy the code

createWatcher

Function createWatcher (VM,// expOrFn,//watch url handler,// monitor function options) {if (isPlainObject(handler)) Handler = handler. Handler} if (typeof handler === 'string') {// If it is a string, $expOrFn (url, url, options)} return url.$watch(expOrFn, url, options)}Copy the code

this.$watch

$watch = function (expOrFn,// expOrFn,// expOrFn,// expOrFn,// expOrFn): function {const vm: 1. Component = this options = options || {} const watcher = new Watcher(vm, expOrFn, cb, If (options.immediate) {// If you define immediate cb.call(vm, Return function unwatchFn () {watcher.teardown()// Unlisten}}Copy the code

This.$watch(‘ Listener name ‘, (newValue,oldValue)=>{}

Watcher

class Watcher{ constructor(vm, expOrFn, cb, options){ if(options) { this.deep = !! Options. deep // If this. User =!! Options. user // Whether user-wathcer this.sync =!! } this.cb = cb // The callback function this.getter = parsePath(expOrFn) // If it is a string object path, }} function parsePath (path) {if (bailre.test (path)) return Const segments = path.split('.') // return function (obj) {// closure returns a function for (let I = 0; i < segments.length; i++) { if (! Obj) return obj = obj[segments[I]] // return obj}}Copy the code

In this case, the parsePath method returns a closure method; Getter is a function. When this.get is called, this. Getter is called and this.vm is passed to read the corresponding property.

Get (){pushTarget(this) // Assign the current listening watcher instance to dep.target, Let value = this.getter.call(this.vm, this.vm) // pass the VM instance to the closure, If (this.deep) {// if there are deep traverse(value) // popTarget() return value, The parameter immediate uses this value}Copy the code

The sample

<template>{{count}}</template>

data(){
    return{
        count:0
    }
},
watch:{
    count(newValue,oldValue){
        
    }
}
Copy the code

The current count dep when {{count}} is read

{watcherList:[render watcher]}Copy the code

When the code executes to initWatch,

pushTarget(this)
this.getter.call(this.vm, this.vm)
...
popTarget()
...
Copy the code

Count dep at this point

{watcherList:[render watcher, listen watcher]}Copy the code

When count changes, the loop executes watcher in deP, executes the callback function, and calls the run method to return the old and new values

GetAndInvoke (cb) {const Value = this.get() // reevaluate if(value! = = this. Value | | isObject (value) | | this. Deep) {const oldValue = this. The value / / the value of the cache before this. Value = the value / / new values If (this.user) {// if user-watcher cb.call(this.vm, value, oldValue) // Pass new and old values in the callback}}}Copy the code