expect(root.hoists.length).toBe(2)
expect(generate(root).code).toMatchSnapshot()
})
+
+ test('clone hoisted array children in HMR mode', () => {
+ const root = transformWithHoist(`<div><span class="hi"></span></div>`, {
+ hmr: true
+ })
+ expect(root.hoists.length).toBe(2)
+ expect(root.codegenNode).toMatchObject({
+ children: {
+ content: '[..._hoisted_2]'
+ }
+ })
+ })
})
})
* 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 {
filename = '',
prefixIdentifiers = false,
hoistStatic = false,
+ hmr = false,
cacheHandlers = false,
nodeTransforms = [],
directiveTransforms = {},
selfName: nameMatch && capitalize(camelize(nameMatch[1])),
prefixIdentifiers,
hoistStatic,
+ hmr,
cacheHandlers,
nodeTransforms,
directiveTransforms,
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
}
}
slotted,
sourceMap: true,
...compilerOptions,
+ hmr: !isProd,
nodeTransforms: nodeTransforms.concat(compilerOptions.nodeTransforms || []),
filename,
onError: e => errors.push(e),
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
rerender(parentId, compileToFunction(`<Child>2</Child>`))
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(
+ `<div v-for="item of 2">
+ <div>1</div>
+ </div>
+ <p>2</p>
+ <p>3</p>`
+ )
+ }
+ createRecord(appId, App)
+
+ render(h(App), root)
+ expect(serializeInner(root)).toBe(
+ `<div><div>1</div></div><div><div>1</div></div><p>2</p><p>3</p>`
+ )
+
+ // move the <p>3</p> into the <div>1</div>
+ rerender(
+ appId,
+ compileToFunction(
+ `<div v-for="item of 2">
+ <div>1<p>3</p></div>
+ </div>
+ <p>2</p>`
+ )
+ )
+ expect(serializeInner(root)).toBe(
+ `<div><div>1<p>3</p></div></div><div><div>1<p>3</p></div></div><p>2</p>`
+ )
+ })
})