fix(setup): setup hook should be called before beforeCreate

fix #12802

Note this commit moves the initialization of injections and props to
before the invocation of beforeCreate. This should not cause breakage
because props and inject normalization has always been done before
beforeCreate, so code that attempts to modifiy props/inject options
inside beforeCreate should have never worked.
This commit is contained in:
Evan You 2022-10-11 16:23:08 +08:00
parent 0d6d972b32
commit e1342df784
3 changed files with 36 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import config from '../config' import config from '../config'
import { initProxy } from './proxy' import { initProxy } from './proxy'
import { initState } from './state' import { initProps, initState } from './state'
import { initRender } from './render' import { initRender } from './render'
import { initEvents } from './events' import { initEvents } from './events'
import { mark, measure } from '../util/perf' import { mark, measure } from '../util/perf'
@ -10,6 +10,7 @@ import { extend, mergeOptions, formatComponentName } from '../util/index'
import type { Component } from 'types/component' import type { Component } from 'types/component'
import type { InternalComponentOptions } from 'types/options' import type { InternalComponentOptions } from 'types/options'
import { EffectScope } from 'v3/reactivity/effectScope' import { EffectScope } from 'v3/reactivity/effectScope'
import { initSetup } from '../../v3/apiSetup'
let uid = 0 let uid = 0
@ -59,8 +60,12 @@ export function initMixin(Vue: typeof Component) {
initLifecycle(vm) initLifecycle(vm)
initEvents(vm) initEvents(vm)
initRender(vm) initRender(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
const opts = vm.$options
initInjections(vm) // resolve injections before data/props initInjections(vm) // resolve injections before data/props
initProps(vm, opts.props)
initSetup(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
initState(vm) initState(vm)
initProvide(vm) // resolve provide after data/props initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') callHook(vm, 'created')

View File

@ -2,7 +2,6 @@ import config from '../config'
import Watcher from '../observer/watcher' import Watcher from '../observer/watcher'
import Dep, { pushTarget, popTarget } from '../observer/dep' import Dep, { pushTarget, popTarget } from '../observer/dep'
import { isUpdatingChildComponent } from './lifecycle' import { isUpdatingChildComponent } from './lifecycle'
import { initSetup } from 'v3/apiSetup'
import { import {
set, set,
@ -51,11 +50,6 @@ export function proxy(target: Object, sourceKey: string, key: string) {
export function initState(vm: Component) { export function initState(vm: Component) {
const opts = vm.$options const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
// Composition API
initSetup(vm)
if (opts.methods) initMethods(vm, opts.methods) if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) { if (opts.data) {
initData(vm) initData(vm)
@ -69,7 +63,8 @@ export function initState(vm: Component) {
} }
} }
function initProps(vm: Component, propsOptions: Object) { export function initProps(vm: Component, propsOptions: Object | undefined) {
if (!propsOptions) return
const propsData = vm.$options.propsData || {} const propsData = vm.$options.propsData || {}
const props = (vm._props = shallowReactive({})) const props = (vm._props = shallowReactive({}))
// cache prop keys so that future props updates can iterate using Array // cache prop keys so that future props updates can iterate using Array

View File

@ -263,7 +263,7 @@ describe('api: setup context', () => {
}).$mount() }).$mount()
expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalled()
}) })
// #12561 // #12561
it('setup props should be reactive', () => { it('setup props should be reactive', () => {
const msg = ref('hi') const msg = ref('hi')
@ -333,4 +333,30 @@ describe('api: setup context', () => {
await nextTick() await nextTick()
expect(_listeners.foo()).toBe(2) expect(_listeners.foo()).toBe(2)
}) })
// #12802
it('should be called before all lifecycle hooks', () => {
const calls: string[] = []
Vue.mixin({
beforeCreate() {
calls.push('global beforeCreate')
}
})
new Vue({
beforeCreate() {
calls.push('component beforeCreate')
},
setup() {
calls.push('setup')
}
})
expect(calls).toEqual([
'setup',
'global beforeCreate',
'component beforeCreate'
])
})
}) })