Browse Source

fix(runtime-core): Avoid mutating original options object in createApp (#4840)

fix #4398
Yuchao 4 years ago
parent
commit
d121a9bc7e

+ 2 - 2
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', () => {

+ 5 - 0
packages/runtime-core/src/apiCreateApp.ts

@@ -179,6 +179,11 @@ export function createAppAPI<HostElement>(
   hydrate?: RootHydrateFunction
 ): CreateAppFunction<HostElement> {
   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

+ 29 - 0
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)
+    
+  })
 })