refactor(compiler): move patchFlag text generation to codegen phase

This commit is contained in:
Evan You 2024-07-13 18:21:02 +08:00
parent f05b3ae0f8
commit aef807746a
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
18 changed files with 194 additions and 211 deletions

View File

@ -54,7 +54,7 @@ return function render(_ctx, _cache) {
[foo + bar]: bar [foo + bar]: bar
}, [ }, [
_createElementVNode("p", { "some-key": "foo" }) _createElementVNode("p", { "some-key": "foo" })
], 16) ], 16 /* FULL_PROPS */)
} }
}" }"
`; `;
@ -98,7 +98,7 @@ exports[`compiler: codegen > forNode 1`] = `
" "
return function render(_ctx, _cache) { return function render(_ctx, _cache) {
with (_ctx) { with (_ctx) {
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1)) return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1 /* TEXT */))
} }
}" }"
`; `;

View File

@ -267,7 +267,7 @@ describe('compiler: codegen', () => {
disableTracking: true, disableTracking: true,
props: undefined, props: undefined,
children: createCallExpression(RENDER_LIST), children: createCallExpression(RENDER_LIST),
patchFlag: '1', patchFlag: PatchFlags.TEXT,
dynamicProps: undefined, dynamicProps: undefined,
directives: undefined, directives: undefined,
loc: locStub, loc: locStub,
@ -303,7 +303,7 @@ describe('compiler: codegen', () => {
disableTracking: false, disableTracking: false,
props: undefined, props: undefined,
children: createCallExpression(RENDER_LIST), children: createCallExpression(RENDER_LIST),
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT), patchFlag: PatchFlags.STABLE_FRAGMENT,
dynamicProps: undefined, dynamicProps: undefined,
directives: undefined, directives: undefined,
loc: locStub, loc: locStub,
@ -364,7 +364,7 @@ describe('compiler: codegen', () => {
), ),
], ],
// flag // flag
PatchFlags.FULL_PROPS + '', PatchFlags.FULL_PROPS,
), ),
}), }),
) )
@ -375,7 +375,7 @@ describe('compiler: codegen', () => {
[foo + bar]: bar [foo + bar]: bar
}, [ }, [
_${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" }) _${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" })
], ${PatchFlags.FULL_PROPS})`) ], ${genFlagText(PatchFlags.FULL_PROPS)})`)
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
@ -666,9 +666,12 @@ describe('compiler: codegen', () => {
}) })
test('with patchFlag and no children/props', () => { test('with patchFlag and no children/props', () => {
expect(genCode(createVNodeCall(null, `"div"`, undefined, undefined, '1'))) expect(
.toMatchInlineSnapshot(` genCode(
"return _createElementVNode("div", null, null, 1) createVNodeCall(null, `"div"`, undefined, undefined, PatchFlags.TEXT),
),
).toMatchInlineSnapshot(`
"return _createElementVNode("div", null, null, 1 /* TEXT */)
" "
`) `)
}) })

View File

@ -19,7 +19,6 @@ import { transformFor } from '../src/transforms/vFor'
import { transformElement } from '../src/transforms/transformElement' import { transformElement } from '../src/transforms/transformElement'
import { transformSlotOutlet } from '../src/transforms/transformSlotOutlet' import { transformSlotOutlet } from '../src/transforms/transformSlotOutlet'
import { transformText } from '../src/transforms/transformText' import { transformText } from '../src/transforms/transformText'
import { genFlagText } from './testUtils'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
describe('compiler: transform', () => { describe('compiler: transform', () => {
@ -358,7 +357,7 @@ describe('compiler: transform', () => {
{ type: NodeTypes.ELEMENT, tag: `div` }, { type: NodeTypes.ELEMENT, tag: `div` },
{ type: NodeTypes.ELEMENT, tag: `div` }, { type: NodeTypes.ELEMENT, tag: `div` },
] as any, ] as any,
genFlagText(PatchFlags.STABLE_FRAGMENT), PatchFlags.STABLE_FRAGMENT,
), ),
) )
}) })
@ -374,10 +373,7 @@ describe('compiler: transform', () => {
{ type: NodeTypes.ELEMENT, tag: `div` }, { type: NodeTypes.ELEMENT, tag: `div` },
{ type: NodeTypes.COMMENT }, { type: NodeTypes.COMMENT },
] as any, ] as any,
genFlagText([ PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT,
PatchFlags.STABLE_FRAGMENT,
PatchFlags.DEV_ROOT_FRAGMENT,
]),
), ),
) )
}) })

View File

@ -21,7 +21,7 @@ import { transformIf } from '../../src/transforms/vIf'
import { transformFor } from '../../src/transforms/vFor' import { transformFor } from '../../src/transforms/vFor'
import { transformBind } from '../../src/transforms/vBind' import { transformBind } from '../../src/transforms/vBind'
import { transformOn } from '../../src/transforms/vOn' import { transformOn } from '../../src/transforms/vOn'
import { createObjectMatcher, genFlagText } from '../testUtils' import { createObjectMatcher } from '../testUtils'
import { transformText } from '../../src/transforms/transformText' import { transformText } from '../../src/transforms/transformText'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
@ -180,7 +180,7 @@ describe('compiler: hoistStatic transform', () => {
id: `[foo]`, id: `[foo]`,
}), }),
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.PROPS), patchFlag: PatchFlags.PROPS,
dynamicProps: { dynamicProps: {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`, content: `_hoisted_1`,
@ -242,7 +242,7 @@ describe('compiler: hoistStatic transform', () => {
ref: `[foo]`, ref: `[foo]`,
}), }),
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH), patchFlag: PatchFlags.NEED_PATCH,
}, },
}, },
]) ])
@ -263,7 +263,7 @@ describe('compiler: hoistStatic transform', () => {
content: `_hoisted_1`, content: `_hoisted_1`,
}, },
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH), patchFlag: PatchFlags.NEED_PATCH,
directives: { directives: {
type: NodeTypes.JS_ARRAY_EXPRESSION, type: NodeTypes.JS_ARRAY_EXPRESSION,
}, },
@ -286,7 +286,7 @@ describe('compiler: hoistStatic transform', () => {
tag: `"div"`, tag: `"div"`,
props: { content: `_hoisted_1` }, props: { content: `_hoisted_1` },
children: { type: NodeTypes.INTERPOLATION }, children: { type: NodeTypes.INTERPOLATION },
patchFlag: genFlagText(PatchFlags.TEXT), patchFlag: PatchFlags.TEXT,
}, },
}, },
]) ])
@ -365,7 +365,7 @@ describe('compiler: hoistStatic transform', () => {
type: NodeTypes.JS_CALL_EXPRESSION, type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST, callee: RENDER_LIST,
}, },
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT), patchFlag: PatchFlags.UNKEYED_FRAGMENT,
}) })
const innerBlockCodegen = forBlockCodegen!.children.arguments[1] const innerBlockCodegen = forBlockCodegen!.children.arguments[1]
expect(innerBlockCodegen.returns).toMatchObject({ expect(innerBlockCodegen.returns).toMatchObject({
@ -496,7 +496,7 @@ describe('compiler: hoistStatic transform', () => {
constType: ConstantTypes.NOT_CONSTANT, constType: ConstantTypes.NOT_CONSTANT,
}, },
}, },
patchFlag: `1 /* TEXT */`, patchFlag: PatchFlags.TEXT,
}, },
}, },
], ],

View File

@ -37,7 +37,7 @@ import { transformStyle } from '../../../compiler-dom/src/transforms/transformSt
import { transformOn } from '../../src/transforms/vOn' import { transformOn } from '../../src/transforms/vOn'
import { transformBind } from '../../src/transforms/vBind' import { transformBind } from '../../src/transforms/vBind'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { createObjectMatcher, genFlagText } from '../testUtils' import { createObjectMatcher } from '../testUtils'
import { transformText } from '../../src/transforms/transformText' import { transformText } from '../../src/transforms/transformText'
import { parseWithForTransform } from './vFor.spec' import { parseWithForTransform } from './vFor.spec'
@ -521,7 +521,7 @@ describe('compiler: element transform', () => {
// keep-alive should not compile content to slots // keep-alive should not compile content to slots
children: [{ type: NodeTypes.ELEMENT, tag: 'span' }], children: [{ type: NodeTypes.ELEMENT, tag: 'span' }],
// should get a dynamic slots flag to force updates // should get a dynamic slots flag to force updates
patchFlag: genFlagText(PatchFlags.DYNAMIC_SLOTS), patchFlag: PatchFlags.DYNAMIC_SLOTS,
}) })
} }
@ -588,7 +588,7 @@ describe('compiler: element transform', () => {
}) })
// should factor in props returned by custom directive transforms // should factor in props returned by custom directive transforms
// in patchFlag analysis // in patchFlag analysis
expect(node.patchFlag).toMatch(PatchFlags.PROPS + '') expect(node.patchFlag).toBe(PatchFlags.PROPS)
expect(node.dynamicProps).toMatch(`"bar"`) expect(node.dynamicProps).toMatch(`"bar"`)
}) })
@ -612,7 +612,7 @@ describe('compiler: element transform', () => {
tag: `"div"`, tag: `"div"`,
props: undefined, props: undefined,
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH), // should generate appropriate flag patchFlag: PatchFlags.NEED_PATCH, // should generate appropriate flag
directives: { directives: {
type: NodeTypes.JS_ARRAY_EXPRESSION, type: NodeTypes.JS_ARRAY_EXPRESSION,
elements: [ elements: [
@ -945,26 +945,26 @@ describe('compiler: element transform', () => {
expect(node.patchFlag).toBeUndefined() expect(node.patchFlag).toBeUndefined()
const { node: node2 } = parseWithBind(`<div>{{ foo }}</div>`) const { node: node2 } = parseWithBind(`<div>{{ foo }}</div>`)
expect(node2.patchFlag).toBe(genFlagText(PatchFlags.TEXT)) expect(node2.patchFlag).toBe(PatchFlags.TEXT)
// multiple nodes, merged with optimize text // multiple nodes, merged with optimize text
const { node: node3 } = parseWithBind(`<div>foo {{ bar }} baz</div>`) const { node: node3 } = parseWithBind(`<div>foo {{ bar }} baz</div>`)
expect(node3.patchFlag).toBe(genFlagText(PatchFlags.TEXT)) expect(node3.patchFlag).toBe(PatchFlags.TEXT)
}) })
test('CLASS', () => { test('CLASS', () => {
const { node } = parseWithBind(`<div :class="foo" />`) const { node } = parseWithBind(`<div :class="foo" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.CLASS)) expect(node.patchFlag).toBe(PatchFlags.CLASS)
}) })
test('STYLE', () => { test('STYLE', () => {
const { node } = parseWithBind(`<div :style="foo" />`) const { node } = parseWithBind(`<div :style="foo" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.STYLE)) expect(node.patchFlag).toBe(PatchFlags.STYLE)
}) })
test('PROPS', () => { test('PROPS', () => {
const { node } = parseWithBind(`<div id="foo" :foo="bar" :baz="qux" />`) const { node } = parseWithBind(`<div id="foo" :foo="bar" :baz="qux" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.PROPS)) expect(node.patchFlag).toBe(PatchFlags.PROPS)
expect(node.dynamicProps).toBe(`["foo", "baz"]`) expect(node.dynamicProps).toBe(`["foo", "baz"]`)
}) })
@ -973,7 +973,7 @@ describe('compiler: element transform', () => {
`<div id="foo" :class="cls" :style="styl" :foo="bar" :baz="qux"/>`, `<div id="foo" :class="cls" :style="styl" :foo="bar" :baz="qux"/>`,
) )
expect(node.patchFlag).toBe( expect(node.patchFlag).toBe(
genFlagText([PatchFlags.CLASS, PatchFlags.STYLE, PatchFlags.PROPS]), PatchFlags.CLASS | PatchFlags.STYLE | PatchFlags.PROPS,
) )
expect(node.dynamicProps).toBe(`["foo", "baz"]`) expect(node.dynamicProps).toBe(`["foo", "baz"]`)
}) })
@ -983,40 +983,40 @@ describe('compiler: element transform', () => {
const { node } = parseWithBind( const { node } = parseWithBind(
`<Foo :id="foo" :class="cls" :style="styl" />`, `<Foo :id="foo" :class="cls" :style="styl" />`,
) )
expect(node.patchFlag).toBe(genFlagText(PatchFlags.PROPS)) expect(node.patchFlag).toBe(PatchFlags.PROPS)
expect(node.dynamicProps).toBe(`["id", "class", "style"]`) expect(node.dynamicProps).toBe(`["id", "class", "style"]`)
}) })
test('FULL_PROPS (v-bind)', () => { test('FULL_PROPS (v-bind)', () => {
const { node } = parseWithBind(`<div v-bind="foo" />`) const { node } = parseWithBind(`<div v-bind="foo" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS)) expect(node.patchFlag).toBe(PatchFlags.FULL_PROPS)
}) })
test('FULL_PROPS (dynamic key)', () => { test('FULL_PROPS (dynamic key)', () => {
const { node } = parseWithBind(`<div :[foo]="bar" />`) const { node } = parseWithBind(`<div :[foo]="bar" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS)) expect(node.patchFlag).toBe(PatchFlags.FULL_PROPS)
}) })
test('FULL_PROPS (w/ others)', () => { test('FULL_PROPS (w/ others)', () => {
const { node } = parseWithBind( const { node } = parseWithBind(
`<div id="foo" v-bind="bar" :class="cls" />`, `<div id="foo" v-bind="bar" :class="cls" />`,
) )
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS)) expect(node.patchFlag).toBe(PatchFlags.FULL_PROPS)
}) })
test('NEED_PATCH (static ref)', () => { test('NEED_PATCH (static ref)', () => {
const { node } = parseWithBind(`<div ref="foo" />`) const { node } = parseWithBind(`<div ref="foo" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH)) expect(node.patchFlag).toBe(PatchFlags.NEED_PATCH)
}) })
test('NEED_PATCH (dynamic ref)', () => { test('NEED_PATCH (dynamic ref)', () => {
const { node } = parseWithBind(`<div :ref="foo" />`) const { node } = parseWithBind(`<div :ref="foo" />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH)) expect(node.patchFlag).toBe(PatchFlags.NEED_PATCH)
}) })
test('NEED_PATCH (custom directives)', () => { test('NEED_PATCH (custom directives)', () => {
const { node } = parseWithBind(`<div v-foo />`) const { node } = parseWithBind(`<div v-foo />`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH)) expect(node.patchFlag).toBe(PatchFlags.NEED_PATCH)
}) })
test('NEED_PATCH (vnode hooks)', () => { test('NEED_PATCH (vnode hooks)', () => {
@ -1025,7 +1025,7 @@ describe('compiler: element transform', () => {
cacheHandlers: true, cacheHandlers: true,
}).ast }).ast
const node = (root as any).children[0].codegenNode const node = (root as any).children[0].codegenNode
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH)) expect(node.patchFlag).toBe(PatchFlags.NEED_PATCH)
}) })
test('script setup inline mode template ref (binding exists)', () => { test('script setup inline mode template ref (binding exists)', () => {
@ -1120,7 +1120,7 @@ describe('compiler: element transform', () => {
}, },
}) })
// should only have props flag // should only have props flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.PROPS)) expect(node.patchFlag).toBe(PatchFlags.PROPS)
const { node: node2 } = parseWithElementTransform( const { node: node2 } = parseWithElementTransform(
`<div @keyup="foo" />`, `<div @keyup="foo" />`,
@ -1130,21 +1130,15 @@ describe('compiler: element transform', () => {
}, },
}, },
) )
expect(node2.patchFlag).toBe( expect(node2.patchFlag).toBe(PatchFlags.PROPS | PatchFlags.NEED_HYDRATION)
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION]),
)
}) })
test('NEED_HYDRATION for v-bind.prop', () => { test('NEED_HYDRATION for v-bind.prop', () => {
const { node } = parseWithBind(`<div v-bind:id.prop="id" />`) const { node } = parseWithBind(`<div v-bind:id.prop="id" />`)
expect(node.patchFlag).toBe( expect(node.patchFlag).toBe(PatchFlags.PROPS | PatchFlags.NEED_HYDRATION)
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION]),
)
const { node: node2 } = parseWithBind(`<div .id="id" />`) const { node: node2 } = parseWithBind(`<div .id="id" />`)
expect(node2.patchFlag).toBe( expect(node2.patchFlag).toBe(PatchFlags.PROPS | PatchFlags.NEED_HYDRATION)
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION]),
)
}) })
// #5870 // #5870
@ -1157,9 +1151,7 @@ describe('compiler: element transform', () => {
}, },
}, },
) )
expect(node.patchFlag).toBe( expect(node.patchFlag).toBe(PatchFlags.PROPS | PatchFlags.NEED_HYDRATION)
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION]),
)
}) })
test('should not have PROPS patchflag for constant v-on handlers', () => { test('should not have PROPS patchflag for constant v-on handlers', () => {
@ -1173,7 +1165,7 @@ describe('compiler: element transform', () => {
}, },
}) })
// should only have hydration flag // should only have hydration flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION)) expect(node.patchFlag).toBe(PatchFlags.NEED_HYDRATION)
}) })
}) })

View File

@ -18,8 +18,8 @@ import {
import { ErrorCodes } from '../../src/errors' import { ErrorCodes } from '../../src/errors'
import { type CompilerOptions, generate } from '../../src' import { type CompilerOptions, generate } from '../../src'
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers' import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
import { PatchFlagNames, PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { createObjectMatcher, genFlagText } from '../testUtils' import { createObjectMatcher } from '../testUtils'
export function parseWithForTransform( export function parseWithForTransform(
template: string, template: string,
@ -696,10 +696,10 @@ describe('compiler: v-for', () => {
tag: FRAGMENT, tag: FRAGMENT,
disableTracking, disableTracking,
patchFlag: !disableTracking patchFlag: !disableTracking
? genFlagText(PatchFlags.STABLE_FRAGMENT) ? PatchFlags.STABLE_FRAGMENT
: keyed : keyed
? genFlagText(PatchFlags.KEYED_FRAGMENT) ? PatchFlags.KEYED_FRAGMENT
: genFlagText(PatchFlags.UNKEYED_FRAGMENT), : PatchFlags.UNKEYED_FRAGMENT,
children: { children: {
type: NodeTypes.JS_CALL_EXPRESSION, type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST, callee: RENDER_LIST,
@ -822,7 +822,7 @@ describe('compiler: v-for', () => {
constType: ConstantTypes.NOT_CONSTANT, constType: ConstantTypes.NOT_CONSTANT,
}, },
}, },
patchFlag: genFlagText(PatchFlags.TEXT), patchFlag: PatchFlags.TEXT,
}, },
}) })
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
@ -846,7 +846,7 @@ describe('compiler: v-for', () => {
{ type: NodeTypes.TEXT, content: `hello` }, { type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }, { type: NodeTypes.ELEMENT, tag: `span` },
], ],
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT), patchFlag: PatchFlags.STABLE_FRAGMENT,
}, },
}) })
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
@ -950,7 +950,7 @@ describe('compiler: v-for', () => {
{ type: NodeTypes.TEXT, content: `hello` }, { type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }, { type: NodeTypes.ELEMENT, tag: `span` },
], ],
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT), patchFlag: PatchFlags.STABLE_FRAGMENT,
}, },
}) })
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
@ -971,7 +971,7 @@ describe('compiler: v-for', () => {
}), }),
isBlock: true, isBlock: true,
disableTracking: true, disableTracking: true,
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT), patchFlag: PatchFlags.UNKEYED_FRAGMENT,
children: { children: {
type: NodeTypes.JS_CALL_EXPRESSION, type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST, callee: RENDER_LIST,
@ -1009,7 +1009,7 @@ describe('compiler: v-for', () => {
}), }),
isBlock: true, isBlock: true,
disableTracking: true, disableTracking: true,
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT), patchFlag: PatchFlags.UNKEYED_FRAGMENT,
children: { children: {
type: NodeTypes.JS_CALL_EXPRESSION, type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST, callee: RENDER_LIST,
@ -1048,9 +1048,7 @@ describe('compiler: v-for', () => {
const { const {
node: { codegenNode }, node: { codegenNode },
} = parseWithForTransform('<div v-for="key in keys" :key>test</div>') } = parseWithForTransform('<div v-for="key in keys" :key>test</div>')
expect(codegenNode.patchFlag).toBe( expect(codegenNode.patchFlag).toBe(PatchFlags.KEYED_FRAGMENT)
`${PatchFlags.KEYED_FRAGMENT} /* ${PatchFlagNames[PatchFlags.KEYED_FRAGMENT]} */`,
)
}) })
test('template v-for key w/ :key shorthand on template injected to the child', () => { test('template v-for key w/ :key shorthand on template injected to the child', () => {

View File

@ -24,7 +24,7 @@ import {
trackVForSlotScopes, trackVForSlotScopes,
} from '../../src/transforms/vSlot' } from '../../src/transforms/vSlot'
import { CREATE_SLOTS, RENDER_LIST } from '../../src/runtimeHelpers' import { CREATE_SLOTS, RENDER_LIST } from '../../src/runtimeHelpers'
import { createObjectMatcher, genFlagText } from '../testUtils' import { createObjectMatcher } from '../testUtils'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { transformFor } from '../../src/transforms/vFor' import { transformFor } from '../../src/transforms/vFor'
import { transformIf } from '../../src/transforms/vIf' import { transformIf } from '../../src/transforms/vIf'
@ -432,7 +432,7 @@ describe('compiler: transform component slots', () => {
), ),
// nested slot should be forced dynamic, since scope variables // nested slot should be forced dynamic, since scope variables
// are not tracked as dependencies of the slot. // are not tracked as dependencies of the slot.
patchFlag: genFlagText(PatchFlags.DYNAMIC_SLOTS), patchFlag: PatchFlags.DYNAMIC_SLOTS,
}, },
}, },
// test scope // test scope
@ -474,9 +474,7 @@ describe('compiler: transform component slots', () => {
const div = ((root.children[0] as ForNode).children[0] as ElementNode) const div = ((root.children[0] as ForNode).children[0] as ElementNode)
.codegenNode as any .codegenNode as any
const comp = div.children[0] const comp = div.children[0]
expect(comp.codegenNode.patchFlag).toBe( expect(comp.codegenNode.patchFlag).toBe(PatchFlags.DYNAMIC_SLOTS)
genFlagText(PatchFlags.DYNAMIC_SLOTS),
)
}) })
test('should only force dynamic slots when actually using scope vars w/ prefixIdentifiers: true', () => { test('should only force dynamic slots when actually using scope vars w/ prefixIdentifiers: true', () => {
@ -494,7 +492,7 @@ describe('compiler: transform component slots', () => {
flag = (innerComp.codegenNode as VNodeCall).patchFlag flag = (innerComp.codegenNode as VNodeCall).patchFlag
} }
if (shouldForce) { if (shouldForce) {
expect(flag).toBe(genFlagText(PatchFlags.DYNAMIC_SLOTS)) expect(flag).toBe(PatchFlags.DYNAMIC_SLOTS)
} else { } else {
expect(flag).toBeUndefined() expect(flag).toBeUndefined()
} }
@ -581,8 +579,8 @@ describe('compiler: transform component slots', () => {
}, },
], ],
}) })
expect((root as any).children[0].codegenNode.patchFlag).toMatch( expect((root as any).children[0].codegenNode.patchFlag).toBe(
PatchFlags.DYNAMIC_SLOTS + '', PatchFlags.DYNAMIC_SLOTS,
) )
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
}) })
@ -630,8 +628,8 @@ describe('compiler: transform component slots', () => {
}, },
], ],
}) })
expect((root as any).children[0].codegenNode.patchFlag).toMatch( expect((root as any).children[0].codegenNode.patchFlag).toBe(
PatchFlags.DYNAMIC_SLOTS + '', PatchFlags.DYNAMIC_SLOTS,
) )
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
}) })
@ -693,8 +691,8 @@ describe('compiler: transform component slots', () => {
}, },
], ],
}) })
expect((root as any).children[0].codegenNode.patchFlag).toMatch( expect((root as any).children[0].codegenNode.patchFlag).toBe(
PatchFlags.DYNAMIC_SLOTS + '', PatchFlags.DYNAMIC_SLOTS,
) )
expect((root as any).children[0].children.length).toBe(3) expect((root as any).children[0].children.length).toBe(3)
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
@ -744,8 +742,8 @@ describe('compiler: transform component slots', () => {
}, },
], ],
}) })
expect((root as any).children[0].codegenNode.patchFlag).toMatch( expect((root as any).children[0].codegenNode.patchFlag).toBe(
PatchFlags.DYNAMIC_SLOTS + '', PatchFlags.DYNAMIC_SLOTS,
) )
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
}) })

View File

@ -1,4 +1,4 @@
import { isString } from '@vue/shared' import { type PatchFlags, isString } from '@vue/shared'
import { import {
CREATE_BLOCK, CREATE_BLOCK,
CREATE_ELEMENT_BLOCK, CREATE_ELEMENT_BLOCK,
@ -331,7 +331,7 @@ export interface VNodeCall extends Node {
| ForRenderListExpression // v-for fragment call | ForRenderListExpression // v-for fragment call
| SimpleExpressionNode // hoisted | SimpleExpressionNode // hoisted
| undefined | undefined
patchFlag: string | undefined patchFlag: PatchFlags | undefined
dynamicProps: string | SimpleExpressionNode | undefined dynamicProps: string | SimpleExpressionNode | undefined
directives: DirectiveArguments | undefined directives: DirectiveArguments | undefined
isBlock: boolean isBlock: boolean
@ -561,7 +561,7 @@ export interface ForCodegenNode extends VNodeCall {
tag: typeof FRAGMENT tag: typeof FRAGMENT
props: undefined props: undefined
children: ForRenderListExpression children: ForRenderListExpression
patchFlag: string patchFlag: PatchFlags
disableTracking: boolean disableTracking: boolean
} }

View File

@ -35,7 +35,13 @@ import {
isSimpleIdentifier, isSimpleIdentifier,
toValidAssetId, toValidAssetId,
} from './utils' } from './utils'
import { isArray, isString, isSymbol } from '@vue/shared' import {
PatchFlagNames,
type PatchFlags,
isArray,
isString,
isSymbol,
} from '@vue/shared'
import { import {
CREATE_COMMENT, CREATE_COMMENT,
CREATE_ELEMENT_VNODE, CREATE_ELEMENT_VNODE,
@ -843,6 +849,28 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
disableTracking, disableTracking,
isComponent, isComponent,
} = node } = node
// add dev annotations to patch flags
let patchFlagString
if (patchFlag) {
if (__DEV__) {
if (patchFlag < 0) {
// special flags (negative and mutually exclusive)
patchFlagString = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`
} else {
// bitwise flags
const flagNames = Object.keys(PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n as PatchFlags])
.join(`, `)
patchFlagString = patchFlag + ` /* ${flagNames} */`
}
} else {
patchFlagString = String(patchFlag)
}
}
if (directives) { if (directives) {
push(helper(WITH_DIRECTIVES) + `(`) push(helper(WITH_DIRECTIVES) + `(`)
} }
@ -857,7 +885,7 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
: getVNodeHelper(context.inSSR, isComponent) : getVNodeHelper(context.inSSR, isComponent)
push(helper(callHelper) + `(`, NewlineType.None, node) push(helper(callHelper) + `(`, NewlineType.None, node)
genNodeList( genNodeList(
genNullableArgs([tag, props, children, patchFlag, dynamicProps]), genNullableArgs([tag, props, children, patchFlagString, dynamicProps]),
context, context,
) )
push(`)`) push(`)`)

View File

@ -382,7 +382,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
helper(FRAGMENT), helper(FRAGMENT),
undefined, undefined,
root.children, root.children,
patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``), patchFlag,
undefined, undefined,
undefined, undefined,
true, true,

View File

@ -70,8 +70,7 @@ function walk(
: getConstantType(child, context) : getConstantType(child, context)
if (constantType > ConstantTypes.NOT_CONSTANT) { if (constantType > ConstantTypes.NOT_CONSTANT) {
if (constantType >= ConstantTypes.CAN_HOIST) { if (constantType >= ConstantTypes.CAN_HOIST) {
;(child.codegenNode as VNodeCall).patchFlag = ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED
PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
child.codegenNode = context.hoist(child.codegenNode!) child.codegenNode = context.hoist(child.codegenNode!)
hoistedCount++ hoistedCount++
continue continue
@ -81,9 +80,9 @@ function walk(
// hoisting. // hoisting.
const codegenNode = child.codegenNode! const codegenNode = child.codegenNode!
if (codegenNode.type === NodeTypes.VNODE_CALL) { if (codegenNode.type === NodeTypes.VNODE_CALL) {
const flag = getPatchFlag(codegenNode) const flag = codegenNode.patchFlag
if ( if (
(!flag || (flag === undefined ||
flag === PatchFlags.NEED_PATCH || flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) && flag === PatchFlags.TEXT) &&
getGeneratedPropsConstantType(child, context) >= getGeneratedPropsConstantType(child, context) >=
@ -179,8 +178,7 @@ export function getConstantType(
) { ) {
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
const flag = getPatchFlag(codegenNode) if (codegenNode.patchFlag === undefined) {
if (!flag) {
let returnType = ConstantTypes.CAN_STRINGIFY let returnType = ConstantTypes.CAN_STRINGIFY
// Element itself has no patch flag. However we still need to check: // Element itself has no patch flag. However we still need to check:
@ -365,8 +363,3 @@ function getNodeProps(node: PlainElementNode) {
return codegenNode.props return codegenNode.props
} }
} }
function getPatchFlag(node: VNodeCall): number | undefined {
const flag = node.patchFlag
return flag ? parseInt(flag, 10) : undefined
}

View File

@ -23,7 +23,6 @@ import {
createVNodeCall, createVNodeCall,
} from '../ast' } from '../ast'
import { import {
PatchFlagNames,
PatchFlags, PatchFlags,
camelize, camelize,
capitalize, capitalize,
@ -101,8 +100,7 @@ export const transformElement: NodeTransform = (node, context) => {
let vnodeProps: VNodeCall['props'] let vnodeProps: VNodeCall['props']
let vnodeChildren: VNodeCall['children'] let vnodeChildren: VNodeCall['children']
let vnodePatchFlag: VNodeCall['patchFlag'] let patchFlag: VNodeCall['patchFlag'] | 0 = 0
let patchFlag: number = 0
let vnodeDynamicProps: VNodeCall['dynamicProps'] let vnodeDynamicProps: VNodeCall['dynamicProps']
let dynamicPropNames: string[] | undefined let dynamicPropNames: string[] | undefined
let vnodeDirectives: VNodeCall['directives'] let vnodeDirectives: VNodeCall['directives']
@ -206,35 +204,16 @@ export const transformElement: NodeTransform = (node, context) => {
} }
// patchFlag & dynamicPropNames // patchFlag & dynamicPropNames
if (patchFlag !== 0) {
if (__DEV__) {
if (patchFlag < 0) {
// special flags (negative and mutually exclusive)
vnodePatchFlag =
patchFlag + ` /* ${PatchFlagNames[patchFlag as PatchFlags]} */`
} else {
// bitwise flags
const flagNames = Object.keys(PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n as PatchFlags])
.join(`, `)
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
}
} else {
vnodePatchFlag = String(patchFlag)
}
if (dynamicPropNames && dynamicPropNames.length) { if (dynamicPropNames && dynamicPropNames.length) {
vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames) vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)
} }
}
node.codegenNode = createVNodeCall( node.codegenNode = createVNodeCall(
context, context,
vnodeTag, vnodeTag,
vnodeProps, vnodeProps,
vnodeChildren, vnodeChildren,
vnodePatchFlag, patchFlag === 0 ? undefined : patchFlag,
vnodeDynamicProps, vnodeDynamicProps,
vnodeDirectives, vnodeDirectives,
!!shouldUseBlock, !!shouldUseBlock,

View File

@ -46,7 +46,7 @@ import {
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
import { validateBrowserExpression } from '../validateExpression' import { validateBrowserExpression } from '../validateExpression'
import { PatchFlagNames, PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { transformBindShorthand } from './vBind' import { transformBindShorthand } from './vBind'
export const transformFor = createStructuralDirectiveTransform( export const transformFor = createStructuralDirectiveTransform(
@ -109,8 +109,7 @@ export const transformFor = createStructuralDirectiveTransform(
helper(FRAGMENT), helper(FRAGMENT),
undefined, undefined,
renderExp, renderExp,
fragmentFlag + fragmentFlag,
(__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``),
undefined, undefined,
undefined, undefined,
true /* isBlock */, true /* isBlock */,
@ -169,10 +168,7 @@ export const transformFor = createStructuralDirectiveTransform(
helper(FRAGMENT), helper(FRAGMENT),
keyProperty ? createObjectExpression([keyProperty]) : undefined, keyProperty ? createObjectExpression([keyProperty]) : undefined,
node.children, node.children,
PatchFlags.STABLE_FRAGMENT + PatchFlags.STABLE_FRAGMENT,
(__DEV__
? ` /* ${PatchFlagNames[PatchFlags.STABLE_FRAGMENT]} */`
: ``),
undefined, undefined,
undefined, undefined,
true, true,

View File

@ -280,7 +280,7 @@ function createChildrenCodegenNode(
helper(FRAGMENT), helper(FRAGMENT),
createObjectExpression([keyProperty]), createObjectExpression([keyProperty]),
children, children,
patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``), patchFlag,
undefined, undefined,
undefined, undefined,
true, true,

View File

@ -6,10 +6,7 @@ import {
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { transformVHtml } from '../../src/transforms/vHtml' import { transformVHtml } from '../../src/transforms/vHtml'
import { transformElement } from '../../../compiler-core/src/transforms/transformElement' import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
import { import { createObjectMatcher } from '../../../compiler-core/__tests__/testUtils'
createObjectMatcher,
genFlagText,
} from '../../../compiler-core/__tests__/testUtils'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { DOMErrorCodes } from '../../src/errors' import { DOMErrorCodes } from '../../src/errors'
@ -34,7 +31,7 @@ describe('compiler: v-html transform', () => {
innerHTML: `[test]`, innerHTML: `[test]`,
}), }),
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.PROPS), patchFlag: PatchFlags.PROPS,
dynamicProps: `["innerHTML"]`, dynamicProps: `["innerHTML"]`,
}) })
}) })
@ -53,7 +50,7 @@ describe('compiler: v-html transform', () => {
innerHTML: `[test]`, innerHTML: `[test]`,
}), }),
children: undefined, // <-- children should have been removed children: undefined, // <-- children should have been removed
patchFlag: genFlagText(PatchFlags.PROPS), patchFlag: PatchFlags.PROPS,
dynamicProps: `["innerHTML"]`, dynamicProps: `["innerHTML"]`,
}) })
}) })

View File

@ -14,7 +14,6 @@ import { transformOn } from '../../src/transforms/vOn'
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers' import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers'
import { transformElement } from '../../../compiler-core/src/transforms/transformElement' import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
import { transformExpression } from '../../../compiler-core/src/transforms/transformExpression' import { transformExpression } from '../../../compiler-core/src/transforms/transformExpression'
import { genFlagText } from '../../../compiler-core/__tests__/testUtils'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
function parseWithVOn(template: string, options: CompilerOptions = {}) { function parseWithVOn(template: string, options: CompilerOptions = {}) {
@ -272,7 +271,7 @@ describe('compiler-dom: transform v-on', () => {
// should not treat cached handler as dynamicProp, so it should have no // should not treat cached handler as dynamicProp, so it should have no
// dynamicProps flags and only the hydration flag // dynamicProps flags and only the hydration flag
expect((root as any).children[0].codegenNode.patchFlag).toBe( expect((root as any).children[0].codegenNode.patchFlag).toBe(
genFlagText(PatchFlags.NEED_HYDRATION), PatchFlags.NEED_HYDRATION,
) )
expect(prop).toMatchObject({ expect(prop).toMatchObject({
key: { key: {
@ -300,6 +299,6 @@ describe('compiler-dom: transform v-on', () => {
}, },
}) })
// should only have hydration flag // should only have hydration flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION)) expect(node.patchFlag).toBe(PatchFlags.NEED_HYDRATION)
}) })
}) })

View File

@ -6,10 +6,7 @@ import {
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { transformVText } from '../../src/transforms/vText' import { transformVText } from '../../src/transforms/vText'
import { transformElement } from '../../../compiler-core/src/transforms/transformElement' import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
import { import { createObjectMatcher } from '../../../compiler-core/__tests__/testUtils'
createObjectMatcher,
genFlagText,
} from '../../../compiler-core/__tests__/testUtils'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
import { DOMErrorCodes } from '../../src/errors' import { DOMErrorCodes } from '../../src/errors'
@ -36,7 +33,7 @@ describe('compiler: v-text transform', () => {
}, },
}), }),
children: undefined, children: undefined,
patchFlag: genFlagText(PatchFlags.PROPS), patchFlag: PatchFlags.PROPS,
dynamicProps: `["textContent"]`, dynamicProps: `["textContent"]`,
}) })
}) })
@ -57,7 +54,7 @@ describe('compiler: v-text transform', () => {
}, },
}), }),
children: undefined, // <-- children should have been removed children: undefined, // <-- children should have been removed
patchFlag: genFlagText(PatchFlags.PROPS), patchFlag: PatchFlags.PROPS,
dynamicProps: `["textContent"]`, dynamicProps: `["textContent"]`,
}) })
}) })

View File

@ -1317,13 +1317,16 @@ describe('SSR hydration', () => {
// #10607 // #10607
test('update component stable slot (prod + optimized mode)', async () => { test('update component stable slot (prod + optimized mode)', async () => {
__DEV__ = false __DEV__ = false
try {
const container = document.createElement('div') const container = document.createElement('div')
container.innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>` container.innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>`
const Comp = { const Comp = {
render(this: any) { render(this: any) {
return ( return (
openBlock(), openBlock(),
createElementBlock('div', null, [renderSlot(this.$slots, 'default')]) createElementBlock('div', null, [
renderSlot(this.$slots, 'default'),
])
) )
}, },
} }
@ -1386,7 +1389,11 @@ describe('SSR hydration', () => {
expect(container.innerHTML).toBe( expect(container.innerHTML).toBe(
`<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>`, `<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>`,
) )
} catch (e) {
throw e
} finally {
__DEV__ = true __DEV__ = true
}
}) })
describe('mismatch handling', () => { describe('mismatch handling', () => {