From e1342df7847a51c75192fec74e94378178e046b0 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2022 16:23:08 +0800 Subject: [PATCH] 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. --- src/core/instance/init.ts | 9 +++++++-- src/core/instance/state.ts | 9 ++------- test/unit/features/v3/apiSetup.spec.ts | 28 +++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/core/instance/init.ts b/src/core/instance/init.ts index 91456c21920..3fdfcf4f0cf 100644 --- a/src/core/instance/init.ts +++ b/src/core/instance/init.ts @@ -1,6 +1,6 @@ import config from '../config' import { initProxy } from './proxy' -import { initState } from './state' +import { initProps, initState } from './state' import { initRender } from './render' import { initEvents } from './events' 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 { InternalComponentOptions } from 'types/options' import { EffectScope } from 'v3/reactivity/effectScope' +import { initSetup } from '../../v3/apiSetup' let uid = 0 @@ -59,8 +60,12 @@ export function initMixin(Vue: typeof Component) { initLifecycle(vm) initEvents(vm) initRender(vm) - callHook(vm, 'beforeCreate', undefined, false /* setContext */) + + const opts = vm.$options initInjections(vm) // resolve injections before data/props + initProps(vm, opts.props) + initSetup(vm) + callHook(vm, 'beforeCreate', undefined, false /* setContext */) initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') diff --git a/src/core/instance/state.ts b/src/core/instance/state.ts index aedb72555c9..52b23cfd430 100644 --- a/src/core/instance/state.ts +++ b/src/core/instance/state.ts @@ -2,7 +2,6 @@ import config from '../config' import Watcher from '../observer/watcher' import Dep, { pushTarget, popTarget } from '../observer/dep' import { isUpdatingChildComponent } from './lifecycle' -import { initSetup } from 'v3/apiSetup' import { set, @@ -51,11 +50,6 @@ export function proxy(target: Object, sourceKey: string, key: string) { export function initState(vm: Component) { const opts = vm.$options - if (opts.props) initProps(vm, opts.props) - - // Composition API - initSetup(vm) - if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { 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 props = (vm._props = shallowReactive({})) // cache prop keys so that future props updates can iterate using Array diff --git a/test/unit/features/v3/apiSetup.spec.ts b/test/unit/features/v3/apiSetup.spec.ts index 11757878e9c..21b83769402 100644 --- a/test/unit/features/v3/apiSetup.spec.ts +++ b/test/unit/features/v3/apiSetup.spec.ts @@ -263,7 +263,7 @@ describe('api: setup context', () => { }).$mount() expect(spy).toHaveBeenCalled() }) - + // #12561 it('setup props should be reactive', () => { const msg = ref('hi') @@ -333,4 +333,30 @@ describe('api: setup context', () => { await nextTick() 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' + ]) + }) })