We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
watch 和 computed一直是我们开发和面试时候经常用的一个点,接下来我们通过源码的解析深入的了解watch的内部机制
watch
computed
我们知道Vue加载的时候会调用initState来初始化state
initState
// vue/src/core/instance/init.js Vue.prototype._init = function (options?: Object) { ... initState(vm) // 初始化state ... }
其实我们的watch的初始化在initState内部实现
// vue/src/core/instance/state.js export function initState (vm: Component) { ... 初始化props、methods、data、computed等 if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
我们接下来看下initWatch是什么名堂
initWatch
function initWatch (vm: Component, watch: Object) { for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { // 如果是数组,遍历调用createWatcher for (let i = 0; i < handler.length; i++) { createWatcher(vm, key, handler[i]) } } else { createWatcher(vm, key, handler) } } }
我们会发现一个很有趣的事情,这里做了一个是否是数组的判断,这是因为mixin机制可以让watch是一个数组的形式
上述代码就是遍历数组或者对象,然后调用createWatcher方法
createWatcher
我们接下来看下createWatcher方法
function createWatcher ( vm: Component, expOrFn: string | Function, handler: any, options?: Object ) { if (isPlainObject(handler)) { // 如果是一个对象 options = handler handler = handler.handler } if (typeof handler === 'string') { // 如果是字符串 handler = vm[handler] } return vm.$watch(expOrFn, handler, options) }
那么问题来了,为什么createWatcher方法中还要进行一层类型判断呢?
那是因为我们有3种watch的用法
watch: { name: { handler() {} }, name() {}, name: 'getName', }
如果是对象的话获取对象的handler方法,如果是字符串则去实例上取这个方法
handler
最后我们来看最关键的一个方法vm.$watch(代码做了删减)
vm.$watch
Vue.prototype.$watch = function( // expOrFn 是 监听的 key,cb 是监听回调,opts 是所有选项 expOrFn, cb, opts ){ // expOrFn 是 监听的 key,cb 是监听的回调,opts 是 监听的所有选项 var watcher = new Watcher(this, expOrFn, cb, opts); // 设定了立即执行,所以马上执行回调 if (opts.immediate) { cb.call(this, watcher.value); } };
首先我们可以发现一点,当我们使用watch的时候,如果设置immediate为true,会立刻将handler执行
immediate
然后我们会发现watch的核心, new Watcher(), 没错,watch就是通过Vue的发布订阅机制来实现的一个功能点,在源码中给 watch中的属性新建一个观察者watcher,然后就可以实现属性变化的时候,执行cd也就是watch中的handler了,如果对这一块不了解的同学可以看下我的关于Vue中MVVM原理的文章手写mvvm 之 实现数据双向绑定
new Watcher()
watcher
最后还有一个小知识点,watch是如何实现深度监听的,也就是我们使用的时候设置的deep(代码有删减)
deep
Watcher.prototype.get = function() { Dep.target= this var value = this.getter(this.vm) if (this.deep) traverse(value) Dep.target= null return value };
我们可以发现,当设置deep为true的时候会走traverse方法
traverse
function _traverse (val, seen) { var i, keys; var isA = Array.isArray(val); if (isA) { // 如果是数组 i = val.length; while (i--) { _traverse(val[i], seen); } } else { // 如果是对象 keys = Object.keys(val); i = keys.length; while (i--) { _traverse(val[keys[i]], seen); } } }
我们可以发现就是一直递归往下查找,这里有一个比较有趣的事情是它通过val[i]和 val[keys[i]] 变相的获取值,因为data里面的数据是具有响应式能力的,每一次获取都会触发getter,然后往对应的dep中添加watcher, 然后就实现深度监听的能力了
val[i]
val[keys[i]]
getter
dep
watch 内部通过为设置的属性生成一个watcher的方式实现数据劫持
深度监听的原理是通过不断的递归,变相的调用data的getter,然后往对应的dep里面添加watcher
The text was updated successfully, but these errors were encountered:
No branches or pull requests
【源码】watch 源码解析
前言
watch
和computed
一直是我们开发和面试时候经常用的一个点,接下来我们通过源码的解析深入的了解watch
的内部机制源码解析
我们知道Vue加载的时候会调用
initState
来初始化state其实我们的
watch
的初始化在initState
内部实现我们接下来看下
initWatch
是什么名堂我们会发现一个很有趣的事情,这里做了一个是否是数组的判断,这是因为mixin机制可以让watch是一个数组的形式
上述代码就是遍历数组或者对象,然后调用
createWatcher
方法我们接下来看下
createWatcher
方法那么问题来了,为什么
createWatcher
方法中还要进行一层类型判断呢?那是因为我们有3种watch的用法
如果是对象的话获取对象的
handler
方法,如果是字符串则去实例上取这个方法最后我们来看最关键的一个方法
vm.$watch
(代码做了删减)首先我们可以发现一点,当我们使用
watch
的时候,如果设置immediate
为true,会立刻将handler执行然后我们会发现
watch
的核心,new Watcher()
, 没错,watch
就是通过Vue的发布订阅机制来实现的一个功能点,在源码中给watch
中的属性新建一个观察者watcher
,然后就可以实现属性变化的时候,执行cd也就是watch
中的handler了,如果对这一块不了解的同学可以看下我的关于Vue中MVVM原理的文章手写mvvm 之 实现数据双向绑定最后还有一个小知识点,
watch
是如何实现深度监听的,也就是我们使用的时候设置的deep
(代码有删减)我们可以发现,当设置
deep
为true的时候会走traverse
方法我们可以发现就是一直递归往下查找,这里有一个比较有趣的事情是它通过
val[i]
和val[keys[i]]
变相的获取值,因为data里面的数据是具有响应式能力的,每一次获取都会触发getter
,然后往对应的dep
中添加watcher
, 然后就实现深度监听的能力了总结
watch
内部通过为设置的属性生成一个watcher
的方式实现数据劫持深度监听的原理是通过不断的递归,变相的调用data的getter,然后往对应的dep里面添加watcher
The text was updated successfully, but these errors were encountered: