diff --git a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts index 44434a25..fdcde463 100644 --- a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts +++ b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts @@ -91,9 +91,9 @@ describe('resolveAssets', () => { const root = nodeOps.createElement('div') app.mount(root) - expect(component1!).toBe(Root) // explicit self name reference + expect(component1!).toMatchObject(Root) // explicit self name reference expect(component2!).toBe(Foo) // successful resolve take higher priority - expect(component3!).toBe(Root) // fallback when resolve fails + expect(component3!).toMatchObject(Root) // fallback when resolve fails }) describe('warning', () => { diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index ae2f5841..98eb8fc8 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -179,6 +179,11 @@ export function createAppAPI( hydrate?: RootHydrateFunction ): CreateAppFunction { return function createApp(rootComponent, rootProps = null) { + + if (!isFunction(rootComponent)) { + rootComponent = { ...rootComponent } + } + if (rootProps != null && !isObject(rootProps)) { __DEV__ && warn(`root props passed to app.mount() must be an object.`) rootProps = null diff --git a/packages/runtime-dom/__tests__/createApp.spec.ts b/packages/runtime-dom/__tests__/createApp.spec.ts index 00c9282e..86b25f5c 100644 --- a/packages/runtime-dom/__tests__/createApp.spec.ts +++ b/packages/runtime-dom/__tests__/createApp.spec.ts @@ -12,4 +12,33 @@ describe('createApp for dom', () => { expect(root.children.length).toBe(1) expect(root.children[0] instanceof SVGElement).toBe(true) }) + + // #4398 + test('should not mutate original root component options object', () => { + + const originalObj = { + data() { + return { + counter: 0 + } + } + } + + const handler = jest.fn(msg => { + expect(msg).toMatch(`Component is missing template or render function`) + }) + + const Root = { ...originalObj} + + const app = createApp(Root) + app.config.warnHandler = handler + app.mount(document.createElement('div')) + + // ensure mount is based on a copy of Root object rather than Root object itself + expect(app._component).not.toBe(Root) + + // ensure no mutation happened to Root object + expect(originalObj).toMatchObject(Root) + + }) })