Skip to content

Commit

Permalink
fix(core): disable dependency collection in lifecycle hooks and data …
Browse files Browse the repository at this point in the history
…getter (vuejs#7596)

This fixes the parent being updated more than necessary due to collecting child props
as dependencies during its own update computation.

fix vuejs#7573
  • Loading branch information
javoski authored and aJean committed Aug 19, 2020
1 parent 75bc3d5 commit 60c5471
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/core/instance/lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createEmptyVNode } from '../vdom/vnode'
import { observerState } from '../observer/index'
import { updateComponentListeners } from './events'
import { resolveSlots } from './render-helpers/resolve-slots'
import { pushTarget, popTarget } from '../observer/dep'

import {
warn,
Expand Down Expand Up @@ -315,6 +316,8 @@ export function deactivateChildComponent (vm: Component, direct?: boolean) {
}

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++) {
Expand All @@ -328,4 +331,5 @@ export function callHook (vm: Component, hook: string) {
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
popTarget()
}
6 changes: 5 additions & 1 deletion src/core/instance/state.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* @flow */

import config from '../config'
import Dep from '../observer/dep'
import Watcher from '../observer/watcher'
import Dep, { pushTarget, popTarget } from '../observer/dep'
import { isUpdatingChildComponent } from './lifecycle'

import {
Expand Down Expand Up @@ -150,11 +150,15 @@ function initData (vm: Component) {
}

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()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/observer/dep.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class Dep {
Dep.target = null
const targetStack = []

export function pushTarget (_target: Watcher) {
export function pushTarget (_target: ?Watcher) {
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
Expand Down
32 changes: 32 additions & 0 deletions test/unit/features/options/data.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,38 @@ describe('Options data', () => {
expect(vm.$refs.test.b).toBe(1)
})

it('props should not be reactive', done => {
let calls = 0
const vm = new Vue({
template: `<child :msg="msg"></child>`,
data: {
msg: 'hello'
},
beforeUpdate () { calls++ },
components: {
child: {
template: `<span>{{ localMsg }}</span>`,
props: ['msg'],
data () {
return { localMsg: this.msg }
},
computed: {
computedMsg () {
return this.msg + ' world'
}
}
}
}
}).$mount()
const child = vm.$children[0]
vm.msg = 'hi'
waitForUpdate(() => {
expect(child.localMsg).toBe('hello')
expect(child.computedMsg).toBe('hi world')
expect(calls).toBe(1)
}).then(done)
})

it('should have access to methods', () => {
const vm = new Vue({
methods: {
Expand Down

0 comments on commit 60c5471

Please sign in to comment.