From 733437691f70ebca8dd6cc3bc8356f5b57d4d5d8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 21 Oct 2023 21:07:55 +0800 Subject: [PATCH] fix(hmr): fix hmr error for hoisted children array in v-for fix #6978 close #7114 --- .../__tests__/transforms/hoistStatic.spec.ts | 12 ++++++ packages/compiler-core/src/options.ts | 7 ++++ packages/compiler-core/src/transform.ts | 2 + .../src/transforms/hoistStatic.ts | 9 ++++- packages/compiler-sfc/src/compileTemplate.ts | 1 + packages/runtime-core/__tests__/hmr.spec.ts | 38 ++++++++++++++++++- 6 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts index eec5a76d363..49ad7ad8982 100644 --- a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts +++ b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts @@ -593,5 +593,17 @@ describe('compiler: hoistStatic transform', () => { expect(root.hoists.length).toBe(2) expect(generate(root).code).toMatchSnapshot() }) + + test('clone hoisted array children in HMR mode', () => { + const root = transformWithHoist(`
`, { + hmr: true + }) + expect(root.hoists.length).toBe(2) + expect(root.codegenNode).toMatchObject({ + children: { + content: '[..._hoisted_2]' + } + }) + }) }) }) diff --git a/packages/compiler-core/src/options.ts b/packages/compiler-core/src/options.ts index 65bbcb36dd6..6f1a8efcff8 100644 --- a/packages/compiler-core/src/options.ts +++ b/packages/compiler-core/src/options.ts @@ -256,6 +256,13 @@ export interface TransformOptions * needed to render inline CSS variables on component root */ ssrCssVars?: string + /** + * Whether to compile the template assuming it needs to handle HMR. + * Some edge cases may need to generate different code for HMR to work + * correctly, e.g. #6938, #7138 + * @internal + */ + hmr?: boolean } export interface CodegenOptions extends SharedTransformCodegenOptions { diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index d26c11bba20..04f85679cae 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -129,6 +129,7 @@ export function createTransformContext( filename = '', prefixIdentifiers = false, hoistStatic = false, + hmr = false, cacheHandlers = false, nodeTransforms = [], directiveTransforms = {}, @@ -155,6 +156,7 @@ export function createTransformContext( selfName: nameMatch && capitalize(camelize(nameMatch[1])), prefixIdentifiers, hoistStatic, + hmr, cacheHandlers, nodeTransforms, directiveTransforms, diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index 5526163c6f9..fd443496ca7 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -140,9 +140,16 @@ function walk( node.codegenNode.type === NodeTypes.VNODE_CALL && isArray(node.codegenNode.children) ) { - node.codegenNode.children = context.hoist( + const hoisted = context.hoist( createArrayExpression(node.codegenNode.children) ) + // #6978, #7138, #7114 + // a hoisted children array inside v-for can caused HMR errors since + // it might be mutated when mounting the v-for list + if (context.hmr) { + hoisted.content = `[...${hoisted.content}]` + } + node.codegenNode.children = hoisted } } diff --git a/packages/compiler-sfc/src/compileTemplate.ts b/packages/compiler-sfc/src/compileTemplate.ts index fbd100c9784..b036619c794 100644 --- a/packages/compiler-sfc/src/compileTemplate.ts +++ b/packages/compiler-sfc/src/compileTemplate.ts @@ -212,6 +212,7 @@ function doCompileTemplate({ slotted, sourceMap: true, ...compilerOptions, + hmr: !isProd, nodeTransforms: nodeTransforms.concat(compilerOptions.nodeTransforms || []), filename, onError: e => errors.push(e), diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index db713a3f276..2e989e368a3 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -20,7 +20,7 @@ const { createRecord, rerender, reload } = __VUE_HMR_RUNTIME__ registerRuntimeCompiler(compileToFunction) function compileToFunction(template: string) { - const { code } = baseCompile(template) + const { code } = baseCompile(template, { hoistStatic: true, hmr: true }) const render = new Function('Vue', code)( runtimeTest ) as InternalRenderFunction @@ -567,4 +567,40 @@ describe('hot module replacement', () => { rerender(parentId, compileToFunction(`2`)) expect(serializeInner(root)).toBe(`2`) }) + + // #6978, #7138, #7114 + test('hoisted children array inside v-for', () => { + const root = nodeOps.createElement('div') + const appId = 'test-app-id' + const App: ComponentOptions = { + __hmrId: appId, + render: compileToFunction( + `
+
1
+
+

2

+

3

` + ) + } + createRecord(appId, App) + + render(h(App), root) + expect(serializeInner(root)).toBe( + `
1
1

2

3

` + ) + + // move the

3

into the
1
+ rerender( + appId, + compileToFunction( + `
+
1

3

+
+

2

` + ) + ) + expect(serializeInner(root)).toBe( + `
1

3

1

3

2

` + ) + }) })