前言
本章项目地址
watch 中每个属性都会new一个用户watcher(new Watcher)
- 在数据初始化得时候 开始
new Watcher, Dep.target 指向此时的用户watcher, 此时该属性中的加入用户watcherdep.addSub.push(watcher)
- 当data中的数据发生变化时, 调用该数据的所有watcher
- Watcher先将老值存起来 数据发生变化时 将新值与老值 返回给表达式(cb)
示例
<div id="app">{{ name }}</div>
<script src="dist/vue.js"></script>
<script>
var vm = new Vue({
data: {
name: 'one'
},
watch: {
}
})
vm.$mount('#app')
vm.$watch('name', function(newValue, oldValue) {
console.log(newValue, oldValue)
})
setTimeout(() => {
vm.name = 'two'
}, 2000)

正题
$watch
export function stateMixin(Vue) {
Vue.prototype.$watch = function(key, handler, options={}) {
options.user = true
new Watcher(this, key, handler, options)
}
}
options.watch 初始化开始执行
export function initState(vm) {
const opts = vm.$options
if (opts.watch) {
initWatch(vm, opts.watch)
}
}
function createWatcher(vm, key, handler) {
return vm.$watch(key, handler)
}
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)
}
}
}
Watcher 类 (关键)
import { pushTarget, popTarget } from './dep'
import { queueWatcher } from './scheduler'
let id = 0
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()
this.user = !!options.user
if (typeof exprOrFn == 'string') {
this.getter = function() {
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
}
this.value = this.get()
}
get() {
pushTarget(this)
const value = this.getter.call(this.vm)
popTarget()
return value
}
update() {
queueWatcher(this)
}
run() {
let newValue = this.get()
let oldValue = this.value
this.value = newValue
if (this.user) {
this.cb.call(this.vm, newValue, oldValue)
}
}
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
Dep 类
let id = 0
class Dep {
constructor() {
this.id = id++
this.subs = []
}
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
完
下一章
vue2核心原理(简易)-computed笔记