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