preface
Project address in this chapter
watch
Each attribute innew
aUser watcher (new watcher)
- Start when the data is initialized
new Watcher
Dep.target refers to the user watcher at this time, and the user watcher is added to this propertydep.addSub.push(watcher)
- When the data in data changes, all watcher’s for that data are called
- Watcher stores the old values and returns the new and old values to the expression cb when the data changes.
The sample
<div id="app">{{ name }}</div>
<script src="dist/vue.js"></script>
<script>
var vm = new Vue({
data: {
name: 'one'
},
/** User watcher several ways */
watch: {
// name(newVal, oldVal) {
// console.log(newVal, oldVal)
// },
// name: [
// function(newVal, oldVal) {
// console.log(newVal, oldVal)
/ /},
// function(a, b) {
// console.log(a, b)
/ /}
/ /,
// 'obj.n'(newVal, oldVal) {
// console.log(newVal, oldVal)
// }
}
})
vm.$mount('#app')
vm.$watch('name'.function(newValue, oldValue) {
console.log(newValue, oldValue)
})
setTimeout(() = > {
vm.name = 'two'
}, 2000)
Copy the code
To the chase
$watch
export function stateMixin(Vue) {
Vue.prototype.$watch = function(key, handler, options={}) {
// User created watcher
options.user = true
new Watcher(this, key, handler, options)
}
}
Copy the code
Options. watch initialization starts
export function initState(vm) {
const opts = vm.$options
if (opts.watch) {
initWatch(vm, opts.watch)
}
}
Copy the code
/** initWatche module */
/ * * *@description Call $watch * /
function createWatcher(vm, key, handler) {
return vm.$watch(key, handler)
}
/ * * *@description Initialize watch to split the function of the watch property *@description Create new Watcher */ for each observation property
function initWatch(vm, watch) {
for (const key in watch) {
const handler = watch[key]
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
Watcher class (Key)
import { pushTarget, popTarget } from './dep'
import { queueWatcher } from './scheduler'
/** Give new Watcher an id */
let id = 0
/ * * *@description Data hijacking expects one property to correspond to multiple Watcher properties */
class Watcher {
constructor(vm, exprOrFn, cb, options) {
this.vm = vm
this.exprOrFn = exprOrFn
this.cb = cb
this.options = options
this.id = id++
this.deps = []
this.depsId = new Set(a)/** user watcher */
this.user = !! options.user// See here to wrap the obtained function name into an expression
if (typeof exprOrFn == 'string') {
this.getter = function() {
// vm value 'obj. N '-> ['obj', 'n'] -> vm['obj']['n']
let path = exprOrFn.split('. ')
let obj = vm
for (let i = 0; i < path.length; i++) {
obj = obj[path[i]]
}
return obj
}
} else {
this.getter = exprOrFn
}
/** User watcher gets the first value */ by default
this.value = this.get()
}
/** render generates vNode dep.push(watcher) and updates view */
get() {
pushTarget(this)
// Look here for hijacked data on the VM to push the user Watcher on this property
const value = this.getter.call(this.vm)
popTarget()
return value
}
update() {
queueWatcher(this)}run() {
/** Consider 1 render component watcher */
/** consider 2 user watcher(newValue oldValue) */
let newValue = this.get()
let oldValue = this.value
this.value = newValue
/** see here user watcher returns the value to cb and calls */
if (this.user) {
this.cb.call(this.vm, newValue, oldValue)
}
}
/** Store the deP and exclude the same attribute from being called multiple times when a vNode is generated
/** Second dep and watcher are no longer stored when attributes change
addDep(dep) {
let id = dep.id
if (!this.depsId.has(id)) {
this.depsId.add(id)
this.deps.push(dep)
dep.addSub(this)}}}export default Watcher
Copy the code
Dep class
/** Each hijacked attribute is uniquely identified */
let id = 0
/ * * *@description Each hijacked attribute new Dep */
class Dep {
constructor() {
this.id = id++
this.subs = []
}
/** dep to watcher */
depend() {
if (Dep.target) {
Dep.target.addDep(this)}}addSub(watcher) {
this.subs.push(watcher)
}
notify() {
this.subs.forEach(watcher= > watcher.update())
}
}
Dep.target = null
let stack = []
export function pushTarget(watcher) {
Dep.target = watcher
stack.push(watcher)
}
export function popTarget() {
stack.pop()
Dep.target = stack[stack.length - 1]}export default Dep
Copy the code
After the
The next chapter is about vuE2 core principles (simple)- Computed Notes