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.