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
在初始化 data 时,当 data 写成函数的形式,会进入 getData 函数
data
getData
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} //... } export function getData (data: Function, vm: Component): any { // #7573 disable dep collection when invoking data getters pushTarget() try { return data.call(vm, vm) } catch (e) { handleError(e, vm, `data()`) return {} } finally { popTarget() } }
这里有个有意思的地方,就是在真正执行 data.call(vm, vm) 取值前有一个 pushTarget() 置空 Dep.target 的操作,取值后再恢复 popTarget()。
data.call(vm, vm)
pushTarget()
Dep.target
popTarget()
Dep.target = null const targetStack = [] export function pushTarget (_target: ?Watcher) { if (Dep.target) targetStack.push(Dep.target) Dep.target = _target } export function popTarget () { Dep.target = targetStack.pop() }
其实这么做的原因已经在源码的注释中写明了,就是为了解决 #7573 这个 issues。
Pitfalls of Vue dependency detection may cause redundant dependencies · Issue #7573 · vuejs/vue
这个 bug 的现象是当子组件 data 写成函数形式并且函数中使用了父组件传给子组件的 props,当父组件中做为 props 传入子组件的那个响应式数据改变时,会触发两次父组件的更新。而且触发两次更新只在数据第一次改变时发生,后续就是正常的只触发一次更新。
props
之所以会这样,是因为执行 data.call(vm, vm) 获取子组件 data 值时,里面使用了 props,此时会触发 props 的 getter,造成 props 收集依赖。由于数据初始化的时机是 beforeCreated -> created 之间,此时还没有进入子组件的渲染阶段(生成渲染 Watcher 是在 mountComponent 中),也就没有子组件的渲染 Watcher。所以这时候 Dep.target 指向的依然是父组件的渲染 Watcher。
getter
mountComponent
最终表现就是父组件的字段更新时,正确触发了一次父组件的渲染 Watcher 的 update,更新子组件的 props 时,又触发了一次父组件的渲染 Watcher 的 update。
而第一次更新后,后续收集依赖时子组件的渲染 Watcher 已经存在,所以不会收集到父组件的渲染 Watcher。
其实不只是这里,子组件的 beforeCreate、created、beforeMount 这三个生命周期钩子函数如果用了 props 的话,也会出现同样的问题,所以在 callHook 函数中也做了同样 Dep.target 置空的操作。
beforeCreate
created
beforeMount
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] if (handlers) { for (let i = 0, j = handlers.length; i < j; i++) { try { handlers[i].call(vm) } catch (e) { handleError(e, vm, `${hook} hook`) } } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } popTarget() }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在初始化
data
时,当data
写成函数的形式,会进入getData
函数这里有个有意思的地方,就是在真正执行
data.call(vm, vm)
取值前有一个pushTarget()
置空Dep.target
的操作,取值后再恢复popTarget()
。其实这么做的原因已经在源码的注释中写明了,就是为了解决 #7573 这个 issues。
Pitfalls of Vue dependency detection may cause redundant dependencies · Issue #7573 · vuejs/vue
这个 bug 的现象是当子组件
data
写成函数形式并且函数中使用了父组件传给子组件的props
,当父组件中做为props
传入子组件的那个响应式数据改变时,会触发两次父组件的更新。而且触发两次更新只在数据第一次改变时发生,后续就是正常的只触发一次更新。之所以会这样,是因为执行
data.call(vm, vm)
获取子组件data
值时,里面使用了props
,此时会触发props
的getter
,造成props
收集依赖。由于数据初始化的时机是 beforeCreated -> created 之间,此时还没有进入子组件的渲染阶段(生成渲染 Watcher 是在mountComponent
中),也就没有子组件的渲染 Watcher。所以这时候Dep.target
指向的依然是父组件的渲染 Watcher。最终表现就是父组件的字段更新时,正确触发了一次父组件的渲染 Watcher 的 update,更新子组件的 props 时,又触发了一次父组件的渲染 Watcher 的 update。
而第一次更新后,后续收集依赖时子组件的渲染 Watcher 已经存在,所以不会收集到父组件的渲染 Watcher。
其实不只是这里,子组件的
beforeCreate
、created
、beforeMount
这三个生命周期钩子函数如果用了 props 的话,也会出现同样的问题,所以在 callHook 函数中也做了同样Dep.target
置空的操作。其实不只是这里,子组件的
beforeCreate
、created
、beforeMount
这三个生命周期钩子函数如果用了 props 的话,也会出现同样的问题,所以在 callHook 函数中也做了同样Dep.target
置空的操作。The text was updated successfully, but these errors were encountered: