vm.$watch

API usage

vm.$watch(expOrFn, callback, [options])

  • The return value:unwatch { Function }
  • Usage: Used to observe an expression orcomputedFunction changes on the vue.js instance, passing new and old data as arguments to the callback function.
  • The options parameter:{deep, immediate}, includingdeepSpecifies whether to observe changes in an object’s internal value,immediateSpecifies whether the callback function is executed immediately
Realize the principle of
Vue.prototype.$watch = function(expOrFn, cb, options) {
  const vm = this
  options = options || {}
  const watcher = new Watcher(vm, expOrFn, cb, options)
  if (options.immediate) {
    cb.call(vm, watcher.value)
  }
  return function unwatch() {
    watcher.teardown()
  }
}
Copy the code

The first parameter of the $watch function is either a function or a string expression, so we need to rewrite the Watcher class:

Class constructor {constructor (vm, expOrFn, cb) {this.vm = vm // newif (typeof expOrFn === 'function') {// Not only can dynamic data be returned, but all accessed data is also observed by Watcher this.getter = expOrFn}else {
      this.getter = parsePath(expOrFn)
    }
    this.cb = cb
    this.value = this.get()
  }
  ...
}
Copy the code

As mentioned earlier, the vm.$watch function finally returns an unwatch function, which, as its name implies, cancels the observation function. Executing the unwatch function is essentially executing the teardown function of the current Watcher instance. Currently we do not record subscribed DEPs internally within Watcher, so we need to rewrite again:

Constructor (vm, expOrFn, cb) {this.vm = vm this.deps = [] // add this.depids = new Set() // addif (typeof expOrFn === 'function') {// Not only can dynamic data be returned, but all accessed data is also observed by Watcher this.getter = expOrFn}else {
      this.getter = parsePath(expOrFn)
    }
    this.cb = cb
    this.value = this.get()
  }
  ...
  addDep (dep) {
    const id = dep.id
    if(! This.depids. has(id)) {this.depids. add(id) this.depids. push(dep) dep.addSub(this)}} // Add id to deplet uid = 0
class Dep {
  constructor() {this.subs = []} addSub (sub) {// Newif(somethingToWatch) {
      somethingToWatch.addDep(this)
    }
    //this.subs.push(sub)
  }
  ...
}

Copy the code

By now, not only does the Dep keep track of which Watcher needs to be notified when the data changes, but the Watcher also keeps track of which Watcher needs to be notified by the Dep. In other words, this is a many-to-many relationship. After the deps attribute is added, the teardown function is easy to implement and will be omitted here.

Finally, take a look at the implementation of the deep parameter. When deep is true, you need to listen for all the children of the current object. The implementation principle is similar to Watcher’s process of listening to a value. By accessing this data, the logic that the current data collection depends on is triggered to collect itself. Therefore, it can be achieved by recursively traversing all the sub-values of the current object to be monitored.

vm.$set

API usage

vm.$set(target, key, value)

  • Usage:targetAdd attributes tokeyIf thetargetIt’s responsive, so it’s newkeyValues are also reactive.
Handling of arrays

Array handling is straightforward and can be implemented directly using the wrapped splice function:

function set(target, key, val) {
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key) 
    target.splice(key, 1, val)
    return val
  }
}
Copy the code
Processing of objects
function set(target, key, val) { ... / / newif (key intarget && ! (keyin Object.prototype)) {
    target[key] = val
    returnVal} const ob = target.__ob__ // whether the isvue flag is an instance of Vue and whether the ob.vmcount flag is the root data objectif(target. _isVue | | (ob && ob. VmCount)) {/ / an error...returnVal}if(! ob) { target[key] = valreturn val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}
Copy the code

vm.$delete

API usage

vm.$delete(target, key)

Since deleting an attribute does not automatically notify dependencies in vue.js, we wrap a vm.$delete to avoid the following ugly code =. = :

delete this.obj.name
this.obj.__ob__.dep.notify()
Copy the code

Here is an implementation of vm.$delete:

function delete(target, key) {
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.splice(key, 1)
    return
  }
  const ob = target.__ob__
  if(! hasOwn(target, key))return
  delete ob[key]
  if(! ob)return
  ob.dep.notify()
}
Copy the code

This series of articles are simple to understand vue.js study notes, interested partners can go to read.