首先看下watch所支持的用法,不然你不知道支持哪些用法。
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
//平时相信都是这种写法居多 函数
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名 字符串, 我是没用过这种。。。冷门,关键源码里面有
b: 'someMethod',
// 深度 watcher 对象
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用 也是对象
d: {
handler: function (val, oldVal) { /* ... */ },
immediate: true
},
// 数组
e: [
function handle1 (val, oldVal) { /* ... */ },
function handle2 (val, oldVal) { /* ... */ }
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
vm.a = 2 // => new: 2, old: 1
所以watch支持的写法有:数组、函数、对象、字符串
this.$watch('name', newName => {...})还可以有这种写法,说明vue2的时候就有vue3的影子了类似hooks那种用法,下面源码有介绍。
function initState(vm) { // 初始化所有状态时
vm._watchers = [] // 当前实例watcher集合
const opts = vm.$options // 合并后的属性
... // 其他状态初始化
if(opts.watch) { // 如果有定义watch属性
initWatch(vm, opts.watch) // 执行初始化方法
}
}
---------------------------------------------------------
function initWatch (vm, watch) { // 初始化方法
for (const key in watch) { // 遍历watch内多个监听属性
const handler = watch[key] // 每一个监听属性的值
if (Array.isArray(handler)) { // 如果该项的值为数组
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i]) // 将每一项使用watcher包装
}
} else {
createWatcher(vm, key, handler) // 不是数组直接使用watcher
}
}
}
---------------------------------------------------------
function createWatcher (vm, expOrFn, handler, options) {
if (isPlainObject(handler)) { // 如果是对象,参数移位
options = handler
handler = handler.handler
}
if (typeof handler === 'string') { // 如果是字符串,表示为方法名
handler = vm[handler] // 获取methods内的方法
}
return vm.$watch(expOrFn, handler, options) // 封装
}
总结:
1.首先在new vue初始化阶段时候, 会在initState函数里面先判断是否有定义watch,有的话就执行initWatch
2.使用for in 遍历watch里面的每个key,这里比较有意思的是:watch支持数组函数,平时我们写法都是单个对象方式定义。如果传了数组函数就遍历该数组,调用createWatcher(vm, key, handler)函数。
3.createWatcher首先对 hanlder
的类型做判断,这里面只有2种判断,对象+字符串
3.1如果传的是对象就拿到它最终的回调函数。
3.2如果是字符串就是方法名,会直接映射到vm上面赋值给handler。
3.3 如果是函数就不用理会,直接vm.$watch
最后调用 vm.$watch(keyOrFn, handler, options)
函数,$watch
是 Vue 原型上的方法,它是在执行 stateMixin
的时候定义的
接下来看下$watch的内部实现:
Vue.prototype.$watch = function(expOrFn, cb, options = {}) {
const vm = this
if (isPlainObject(cb)) { // 如果cb是对象,当手动创建监听属性时
return createWatcher(vm, expOrFn, cb, options)
}
options.user = true // user-watcher的标志位,传入Watcher类中
const watcher = new Watcher(vm, expOrFn, cb, options) // 实例化user-watcher
if (options.immediate) { // 立即执行
cb.call(vm, watcher.value) // 以当前值立即执行一次回调函数
} // watcher.value为实例化后返回的值
return function unwatchFn () { // 返回一个函数,执行取消监听
watcher.teardown()
}
}
---------------------------------------------------------------
总结下上面源码发现,this.$watch第二个参数可以传对象
export default {
data() {
return {
name: 'cc'
}
},
created() {
第一种传对象:
let obj={
handler: function (val, oldVal) { /* ... */ },
deep: true
}
this.unwatch1 = this.$watch('name', obj)
第二种传函数:
this.unwatch = this.$watch('name', newName => {...})
this.unwatch() // 取消监听 最后会返回一个函数取消
}
}