test: tests for hoistStatic
This commit is contained in:
parent
802ecccc49
commit
57a5c61320
@ -7,7 +7,7 @@ import {
|
|||||||
ElementCodegenNode
|
ElementCodegenNode
|
||||||
} from '../src'
|
} from '../src'
|
||||||
import { CREATE_VNODE } from '../src/runtimeHelpers'
|
import { CREATE_VNODE } from '../src/runtimeHelpers'
|
||||||
import { isString } from '@vue/shared'
|
import { isString, PatchFlags, PatchFlagNames, isArray } from '@vue/shared'
|
||||||
|
|
||||||
const leadingBracketRE = /^\[/
|
const leadingBracketRE = /^\[/
|
||||||
const bracketsRE = /^\[|\]$/g
|
const bracketsRE = /^\[|\]$/g
|
||||||
@ -58,3 +58,15 @@ export function createElementWithCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function genFlagText(flag: PatchFlags | PatchFlags[]) {
|
||||||
|
if (isArray(flag)) {
|
||||||
|
let f = 0
|
||||||
|
flag.forEach(ff => {
|
||||||
|
f |= ff
|
||||||
|
})
|
||||||
|
return `${f} /* ${flag.map(f => PatchFlagNames[f]).join(', ')} */`
|
||||||
|
} else {
|
||||||
|
return `${flag} /* ${PatchFlagNames[flag]} */`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,205 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist nested static tree 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = _createVNode(\\"p\\", null, [
|
||||||
|
_createVNode(\\"span\\"),
|
||||||
|
_createVNode(\\"span\\")
|
||||||
|
])
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_hoisted_1
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist siblings with common non-hoistable parent 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = _createVNode(\\"span\\")
|
||||||
|
const _hoisted_2 = _createVNode(\\"div\\")
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_hoisted_1,
|
||||||
|
_hoisted_2
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist simple element 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\")
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_hoisted_1
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist static props for elements with directives 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = { id: \\"foo\\" }
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, applyDirectives: _applyDirectives, resolveDirective: _resolveDirective, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
const _directive_foo = _resolveDirective(\\"foo\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_applyDirectives(_createVNode(\\"div\\", _hoisted_1, null, 32 /* NEED_PATCH */), [
|
||||||
|
[_directive_foo]
|
||||||
|
])
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist static props for elements with dynamic text children 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = { id: \\"foo\\" }
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { toString: _toString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_createVNode(\\"div\\", _hoisted_1, _toString(hello), 1 /* TEXT */)
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform hoist static props for elements with unhoistable children 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = { id: \\"foo\\" }
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
const _component_Comp = _resolveComponent(\\"Comp\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_createVNode(\\"div\\", _hoisted_1, [
|
||||||
|
_createVNode(_component_Comp)
|
||||||
|
])
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform should NOT hoist components 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
const _component_Comp = _resolveComponent(\\"Comp\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_createVNode(_component_Comp)
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform should NOT hoist element with dynamic props 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
_createVNode(\\"div\\", { id: foo }, null, 8 /* PROPS */, [\\"id\\"])
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform should NOT hoist root node 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\"))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform should hoist v-for children if static 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = { id: \\"foo\\" }
|
||||||
|
const _hoisted_2 = _createVNode(\\"span\\")
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
(_openBlock(), _createBlock(_Fragment, null, _renderList(list, (i) => {
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [
|
||||||
|
_hoisted_2
|
||||||
|
]))
|
||||||
|
}), 128 /* UNKEYED_FRAGMENT */))
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: hositStatic transform should hoist v-if props/children if static 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
const _createVNode = Vue.createVNode
|
||||||
|
|
||||||
|
const _hoisted_1 = {
|
||||||
|
key: 0,
|
||||||
|
id: \\"foo\\"
|
||||||
|
}
|
||||||
|
const _hoisted_2 = _createVNode(\\"span\\")
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
|
(_openBlock(), ok
|
||||||
|
? _createBlock(\\"div\\", _hoisted_1, [
|
||||||
|
_hoisted_2
|
||||||
|
])
|
||||||
|
: _createBlock(_Empty))
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
359
packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
Normal file
359
packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
import { parse, transform, NodeTypes, generate } from '../../src'
|
||||||
|
import {
|
||||||
|
OPEN_BLOCK,
|
||||||
|
CREATE_BLOCK,
|
||||||
|
CREATE_VNODE,
|
||||||
|
APPLY_DIRECTIVES,
|
||||||
|
FRAGMENT,
|
||||||
|
RENDER_LIST
|
||||||
|
} from '../../src/runtimeHelpers'
|
||||||
|
import { transformElement } from '../../src/transforms/transformElement'
|
||||||
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
|
import { transformFor } from '../../src/transforms/vFor'
|
||||||
|
import { transformBind } from '../../src/transforms/vBind'
|
||||||
|
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||||
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
|
||||||
|
function transformWithHoist(template: string) {
|
||||||
|
const ast = parse(template)
|
||||||
|
transform(ast, {
|
||||||
|
hoistStatic: true,
|
||||||
|
nodeTransforms: [transformIf, transformFor, transformElement],
|
||||||
|
directiveTransforms: {
|
||||||
|
bind: transformBind
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(ast.codegenNode).toMatchObject({
|
||||||
|
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||||
|
expressions: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: OPEN_BLOCK
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: CREATE_BLOCK
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
root: ast,
|
||||||
|
args: (ast.codegenNode as any).expressions[1].arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('compiler: hositStatic transform', () => {
|
||||||
|
test('should NOT hoist root node', () => {
|
||||||
|
// if the whole tree is static, the root still needs to be a block
|
||||||
|
// so that it's patched in optimized mode to skip children
|
||||||
|
const { root, args } = transformWithHoist(`<div/>`)
|
||||||
|
expect(root.hoists.length).toBe(0)
|
||||||
|
expect(args).toEqual([`"div"`])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist simple element', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><span class="inline">hello</span></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"span"`,
|
||||||
|
createObjectMatcher({ class: 'inline' }),
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: `hello`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(args).toMatchObject([
|
||||||
|
`"div"`,
|
||||||
|
`null`,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_hoisted_1`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist nested static tree', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><p><span/><span/></p></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"p"`,
|
||||||
|
`null`,
|
||||||
|
[
|
||||||
|
{ type: NodeTypes.ELEMENT, tag: `span` },
|
||||||
|
{ type: NodeTypes.ELEMENT, tag: `span` }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_hoisted_1`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist siblings with common non-hoistable parent', () => {
|
||||||
|
const { root, args } = transformWithHoist(`<div><span/><div/></div>`)
|
||||||
|
expect(root.hoists).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`"span"`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`"div"`]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_hoisted_1`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_hoisted_2`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should NOT hoist components', () => {
|
||||||
|
const { root, args } = transformWithHoist(`<div><Comp/></div>`)
|
||||||
|
expect(root.hoists.length).toBe(0)
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`_component_Comp`]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should NOT hoist element with dynamic props', () => {
|
||||||
|
const { root, args } = transformWithHoist(`<div><div :id="foo"/></div>`)
|
||||||
|
expect(root.hoists.length).toBe(0)
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
createObjectMatcher({
|
||||||
|
id: `[foo]`
|
||||||
|
}),
|
||||||
|
`null`,
|
||||||
|
genFlagText(PatchFlags.PROPS),
|
||||||
|
`["id"]`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist static props for elements with directives', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><div id="foo" v-foo/></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
callee: APPLY_DIRECTIVES,
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
{
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_hoisted_1`
|
||||||
|
},
|
||||||
|
`null`,
|
||||||
|
genFlagText(PatchFlags.NEED_PATCH)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_ARRAY_EXPRESSION
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist static props for elements with dynamic text children', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><div id="foo">{{ hello }}</div></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
{ content: `_hoisted_1` },
|
||||||
|
{ type: NodeTypes.INTERPOLATION },
|
||||||
|
genFlagText(PatchFlags.TEXT)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoist static props for elements with unhoistable children', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><div id="foo"><Comp/></div></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
|
||||||
|
expect(args[2]).toMatchObject([
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
codegenNode: {
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
{ content: `_hoisted_1` },
|
||||||
|
[{ type: NodeTypes.ELEMENT, tag: `Comp` }]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should hoist v-if props/children if static', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><div v-if="ok" id="foo"><span/></div></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([
|
||||||
|
createObjectMatcher({
|
||||||
|
key: `[0]`, // key injected by v-if branch
|
||||||
|
id: 'foo'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`"span"`]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(args[2][0].codegenNode).toMatchObject({
|
||||||
|
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||||
|
expressions: [
|
||||||
|
{ callee: OPEN_BLOCK },
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
|
||||||
|
consequent: {
|
||||||
|
// blocks should NOT be hoisted
|
||||||
|
callee: CREATE_BLOCK,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
{ content: `_hoisted_1` },
|
||||||
|
[
|
||||||
|
{
|
||||||
|
codegenNode: { content: `_hoisted_2` }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should hoist v-for children if static', () => {
|
||||||
|
const { root, args } = transformWithHoist(
|
||||||
|
`<div><div v-for="i in list" id="foo"><span/></div></div>`
|
||||||
|
)
|
||||||
|
expect(root.hoists).toMatchObject([
|
||||||
|
createObjectMatcher({
|
||||||
|
id: 'foo'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`"span"`]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const forBlockCodegen = args[2][0].codegenNode
|
||||||
|
expect(forBlockCodegen).toMatchObject({
|
||||||
|
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||||
|
expressions: [
|
||||||
|
{ callee: OPEN_BLOCK },
|
||||||
|
{
|
||||||
|
callee: CREATE_BLOCK,
|
||||||
|
arguments: [
|
||||||
|
FRAGMENT,
|
||||||
|
`null`,
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: RENDER_LIST
|
||||||
|
},
|
||||||
|
genFlagText(PatchFlags.UNKEYED_FRAGMENT)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const innerBlockCodegen =
|
||||||
|
forBlockCodegen.expressions[1].arguments[2].arguments[1].returns
|
||||||
|
expect(innerBlockCodegen).toMatchObject({
|
||||||
|
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||||
|
expressions: [
|
||||||
|
{ callee: OPEN_BLOCK },
|
||||||
|
{
|
||||||
|
callee: CREATE_BLOCK,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
{ content: `_hoisted_1` },
|
||||||
|
[
|
||||||
|
{
|
||||||
|
codegenNode: { content: `_hoisted_2` }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
@ -25,7 +25,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 } from '../testUtils'
|
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||||
import { optimizeText } from '../../src/transforms/optimizeText'
|
import { optimizeText } from '../../src/transforms/optimizeText'
|
||||||
|
|
||||||
function parseWithElementTransform(
|
function parseWithElementTransform(
|
||||||
@ -324,7 +324,7 @@ describe('compiler: element transform', () => {
|
|||||||
`"div"`,
|
`"div"`,
|
||||||
`null`,
|
`null`,
|
||||||
`null`,
|
`null`,
|
||||||
`${PatchFlags.NEED_PATCH} /* NEED_PATCH */` // should generate appropriate flag
|
genFlagText(PatchFlags.NEED_PATCH) // should generate appropriate flag
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -573,30 +573,30 @@ describe('compiler: element transform', () => {
|
|||||||
|
|
||||||
const { node: node2 } = parseWithBind(`<div>{{ foo }}</div>`)
|
const { node: node2 } = parseWithBind(`<div>{{ foo }}</div>`)
|
||||||
expect(node2.arguments.length).toBe(4)
|
expect(node2.arguments.length).toBe(4)
|
||||||
expect(node2.arguments[3]).toBe(`${PatchFlags.TEXT} /* TEXT */`)
|
expect(node2.arguments[3]).toBe(genFlagText(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.arguments.length).toBe(4)
|
expect(node3.arguments.length).toBe(4)
|
||||||
expect(node3.arguments[3]).toBe(`${PatchFlags.TEXT} /* TEXT */`)
|
expect(node3.arguments[3]).toBe(genFlagText(PatchFlags.TEXT))
|
||||||
})
|
})
|
||||||
|
|
||||||
test('CLASS', () => {
|
test('CLASS', () => {
|
||||||
const { node } = parseWithBind(`<div :class="foo" />`)
|
const { node } = parseWithBind(`<div :class="foo" />`)
|
||||||
expect(node.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(`${PatchFlags.CLASS} /* CLASS */`)
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.CLASS))
|
||||||
})
|
})
|
||||||
|
|
||||||
test('STYLE', () => {
|
test('STYLE', () => {
|
||||||
const { node } = parseWithBind(`<div :style="foo" />`)
|
const { node } = parseWithBind(`<div :style="foo" />`)
|
||||||
expect(node.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(`${PatchFlags.STYLE} /* STYLE */`)
|
expect(node.arguments[3]).toBe(genFlagText(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.arguments.length).toBe(5)
|
expect(node.arguments.length).toBe(5)
|
||||||
expect(node.arguments[3]).toBe(`${PatchFlags.PROPS} /* PROPS */`)
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.PROPS))
|
||||||
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
|
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -606,9 +606,7 @@ describe('compiler: element transform', () => {
|
|||||||
)
|
)
|
||||||
expect(node.arguments.length).toBe(5)
|
expect(node.arguments.length).toBe(5)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(
|
||||||
`${PatchFlags.PROPS |
|
genFlagText([PatchFlags.CLASS, PatchFlags.STYLE, PatchFlags.PROPS])
|
||||||
PatchFlags.CLASS |
|
|
||||||
PatchFlags.STYLE} /* CLASS, STYLE, PROPS */`
|
|
||||||
)
|
)
|
||||||
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
|
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
|
||||||
})
|
})
|
||||||
@ -616,17 +614,13 @@ describe('compiler: element transform', () => {
|
|||||||
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.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
|
||||||
`${PatchFlags.FULL_PROPS} /* 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.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
|
||||||
`${PatchFlags.FULL_PROPS} /* FULL_PROPS */`
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('FULL_PROPS (w/ others)', () => {
|
test('FULL_PROPS (w/ others)', () => {
|
||||||
@ -634,34 +628,26 @@ describe('compiler: element transform', () => {
|
|||||||
`<div id="foo" v-bind="bar" :class="cls" />`
|
`<div id="foo" v-bind="bar" :class="cls" />`
|
||||||
)
|
)
|
||||||
expect(node.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
|
||||||
`${PatchFlags.FULL_PROPS} /* 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.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
|
||||||
`${PatchFlags.NEED_PATCH} /* 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.arguments.length).toBe(4)
|
expect(node.arguments.length).toBe(4)
|
||||||
expect(node.arguments[3]).toBe(
|
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
|
||||||
`${PatchFlags.NEED_PATCH} /* NEED_PATCH */`
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('NEED_PATCH (custom directives)', () => {
|
test('NEED_PATCH (custom directives)', () => {
|
||||||
const { node } = parseWithBind(`<div v-foo />`)
|
const { node } = parseWithBind(`<div v-foo />`)
|
||||||
const vnodeCall = node.arguments[0] as CallExpression
|
const vnodeCall = node.arguments[0] as CallExpression
|
||||||
expect(vnodeCall.arguments.length).toBe(4)
|
expect(vnodeCall.arguments.length).toBe(4)
|
||||||
expect(vnodeCall.arguments[3]).toBe(
|
expect(vnodeCall.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
|
||||||
`${PatchFlags.NEED_PATCH} /* NEED_PATCH */`
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -25,8 +25,7 @@ import {
|
|||||||
RENDER_SLOT
|
RENDER_SLOT
|
||||||
} from '../../src/runtimeHelpers'
|
} from '../../src/runtimeHelpers'
|
||||||
import { PatchFlags } from '@vue/runtime-dom'
|
import { PatchFlags } from '@vue/runtime-dom'
|
||||||
import { PatchFlagNames } from '@vue/shared'
|
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||||
import { createObjectMatcher } from '../testUtils'
|
|
||||||
|
|
||||||
function parseWithForTransform(
|
function parseWithForTransform(
|
||||||
template: string,
|
template: string,
|
||||||
@ -609,12 +608,8 @@ describe('compiler: v-for', () => {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
keyed
|
keyed
|
||||||
? `${PatchFlags.KEYED_FRAGMENT} /* ${
|
? genFlagText(PatchFlags.KEYED_FRAGMENT)
|
||||||
PatchFlagNames[PatchFlags.KEYED_FRAGMENT]
|
: genFlagText(PatchFlags.UNKEYED_FRAGMENT)
|
||||||
} */`
|
|
||||||
: `${PatchFlags.UNKEYED_FRAGMENT} /* ${
|
|
||||||
PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT]
|
|
||||||
} */`
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -842,9 +837,7 @@ describe('compiler: v-for', () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
`${PatchFlags.UNKEYED_FRAGMENT} /* ${
|
genFlagText(PatchFlags.UNKEYED_FRAGMENT)
|
||||||
PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT]
|
|
||||||
} */`
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ 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 } from '../testUtils'
|
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||||
import { PatchFlags, PatchFlagNames } 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'
|
||||||
|
|
||||||
@ -308,9 +308,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.
|
||||||
`${PatchFlags.DYNAMIC_SLOTS} /* ${
|
genFlagText(PatchFlags.DYNAMIC_SLOTS)
|
||||||
PatchFlagNames[PatchFlags.DYNAMIC_SLOTS]
|
|
||||||
} */`
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -136,6 +136,10 @@ export interface SlotOutletNode extends BaseElementNode {
|
|||||||
|
|
||||||
export interface TemplateNode extends BaseElementNode {
|
export interface TemplateNode extends BaseElementNode {
|
||||||
tagType: ElementTypes.TEMPLATE
|
tagType: ElementTypes.TEMPLATE
|
||||||
|
codegenNode:
|
||||||
|
| ElementCodegenNode
|
||||||
|
| CodegenNodeWithDirective<ElementCodegenNode>
|
||||||
|
| undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextNode extends Node {
|
export interface TextNode extends Node {
|
||||||
@ -279,7 +283,6 @@ export interface ConditionalExpression extends Node {
|
|||||||
export interface ElementCodegenNode extends CallExpression {
|
export interface ElementCodegenNode extends CallExpression {
|
||||||
callee: typeof CREATE_VNODE
|
callee: typeof CREATE_VNODE
|
||||||
arguments: // tag, props, children, patchFlag, dynamicProps
|
arguments: // tag, props, children, patchFlag, dynamicProps
|
||||||
|
|
||||||
| [string | RuntimeHelper]
|
| [string | RuntimeHelper]
|
||||||
| [string | RuntimeHelper, PropsExpression]
|
| [string | RuntimeHelper, PropsExpression]
|
||||||
| [string | RuntimeHelper, 'null' | PropsExpression, TemplateChildNode[]]
|
| [string | RuntimeHelper, 'null' | PropsExpression, TemplateChildNode[]]
|
||||||
@ -306,7 +309,6 @@ export type ElementCodegenNodeWithDirective = CodegenNodeWithDirective<
|
|||||||
export interface ComponentCodegenNode extends CallExpression {
|
export interface ComponentCodegenNode extends CallExpression {
|
||||||
callee: typeof CREATE_VNODE
|
callee: typeof CREATE_VNODE
|
||||||
arguments: // Comp, props, slots, patchFlag, dynamicProps
|
arguments: // Comp, props, slots, patchFlag, dynamicProps
|
||||||
|
|
||||||
| [string | RuntimeHelper]
|
| [string | RuntimeHelper]
|
||||||
| [string | RuntimeHelper, PropsExpression]
|
| [string | RuntimeHelper, PropsExpression]
|
||||||
| [string | RuntimeHelper, 'null' | PropsExpression, SlotsExpression]
|
| [string | RuntimeHelper, 'null' | PropsExpression, SlotsExpression]
|
||||||
@ -395,7 +397,6 @@ export interface DirectiveArguments extends ArrayExpression {
|
|||||||
|
|
||||||
export interface DirectiveArgumentNode extends ArrayExpression {
|
export interface DirectiveArgumentNode extends ArrayExpression {
|
||||||
elements: // dir, exp, arg, modifiers
|
elements: // dir, exp, arg, modifiers
|
||||||
|
|
||||||
| [string]
|
| [string]
|
||||||
| [string, ExpressionNode]
|
| [string, ExpressionNode]
|
||||||
| [string, ExpressionNode, ExpressionNode]
|
| [string, ExpressionNode, ExpressionNode]
|
||||||
@ -406,7 +407,6 @@ export interface DirectiveArgumentNode extends ArrayExpression {
|
|||||||
export interface SlotOutletCodegenNode extends CallExpression {
|
export interface SlotOutletCodegenNode extends CallExpression {
|
||||||
callee: typeof RENDER_SLOT
|
callee: typeof RENDER_SLOT
|
||||||
arguments: // $slots, name, props, fallback
|
arguments: // $slots, name, props, fallback
|
||||||
|
|
||||||
| [string, string | ExpressionNode]
|
| [string, string | ExpressionNode]
|
||||||
| [string, string | ExpressionNode, PropsExpression]
|
| [string, string | ExpressionNode, PropsExpression]
|
||||||
| [
|
| [
|
||||||
@ -557,7 +557,7 @@ type InferCodegenNodeType<T> = T extends typeof CREATE_VNODE
|
|||||||
: T extends typeof CREATE_BLOCK
|
: T extends typeof CREATE_BLOCK
|
||||||
? BlockElementCodegenNode | BlockComponentCodegenNode
|
? BlockElementCodegenNode | BlockComponentCodegenNode
|
||||||
: T extends typeof APPLY_DIRECTIVES
|
: T extends typeof APPLY_DIRECTIVES
|
||||||
? CodegenNodeWithDirective<ElementCodegenNode | ComponentCodegenNode>
|
? ElementCodegenNodeWithDirective | CompoenntCodegenNodeWithDirective
|
||||||
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
|
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
|
||||||
|
|
||||||
export function createCallExpression<T extends CallExpression['callee']>(
|
export function createCallExpression<T extends CallExpression['callee']>(
|
||||||
|
@ -22,8 +22,8 @@ import {
|
|||||||
RuntimeHelper,
|
RuntimeHelper,
|
||||||
helperNameMap
|
helperNameMap
|
||||||
} from './runtimeHelpers'
|
} from './runtimeHelpers'
|
||||||
import { isVSlot, createBlockExpression, isSlotOutlet } from './utils'
|
import { isVSlot, createBlockExpression } from './utils'
|
||||||
import { hoistStatic } from './transforms/hoistStatic'
|
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
|
||||||
|
|
||||||
// There are two types of transforms:
|
// There are two types of transforms:
|
||||||
//
|
//
|
||||||
@ -229,26 +229,19 @@ export function transform(root: RootNode, options: TransformOptions) {
|
|||||||
function finalizeRoot(root: RootNode, context: TransformContext) {
|
function finalizeRoot(root: RootNode, context: TransformContext) {
|
||||||
const { helper } = context
|
const { helper } = context
|
||||||
const { children } = root
|
const { children } = root
|
||||||
if (children.length === 1) {
|
|
||||||
const child = children[0]
|
const child = children[0]
|
||||||
if (
|
if (isSingleElementRoot(root, child) && child.codegenNode) {
|
||||||
child.type === NodeTypes.ELEMENT &&
|
|
||||||
!isSlotOutlet(child) &&
|
|
||||||
child.codegenNode &&
|
|
||||||
child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION
|
|
||||||
) {
|
|
||||||
// turn root element into a block
|
// turn root element into a block
|
||||||
root.codegenNode = createBlockExpression(
|
root.codegenNode = createBlockExpression(
|
||||||
child.codegenNode!.arguments,
|
child.codegenNode.arguments,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
} else {
|
} else if (children.length === 1) {
|
||||||
// - single <slot/>, IfNode, ForNode: already blocks.
|
// - single <slot/>, IfNode, ForNode: already blocks.
|
||||||
// - single text node: always patched.
|
// - single text node: always patched.
|
||||||
// - transform calls without transformElement (only during tests)
|
// - transform calls without transformElement (only during tests)
|
||||||
// Just generate the node as-is
|
// Just generate the node as-is
|
||||||
root.codegenNode = child
|
root.codegenNode = child
|
||||||
}
|
|
||||||
} else if (children.length > 1) {
|
} else if (children.length > 1) {
|
||||||
// root has multiple nodes - return a fragment block.
|
// root has multiple nodes - return a fragment block.
|
||||||
root.codegenNode = createBlockExpression(
|
root.codegenNode = createBlockExpression(
|
||||||
@ -256,7 +249,6 @@ function finalizeRoot(root: RootNode, context: TransformContext) {
|
|||||||
context
|
context
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// finalize meta information
|
// finalize meta information
|
||||||
root.helpers = [...context.helpers]
|
root.helpers = [...context.helpers]
|
||||||
root.components = [...context.components]
|
root.components = [...context.components]
|
||||||
|
@ -5,20 +5,42 @@ import {
|
|||||||
ElementNode,
|
ElementNode,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
ElementCodegenNode,
|
ElementCodegenNode,
|
||||||
ElementCodegenNodeWithDirective
|
ElementCodegenNodeWithDirective,
|
||||||
|
PlainElementNode,
|
||||||
|
ComponentNode,
|
||||||
|
TemplateNode
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { TransformContext } from '../transform'
|
import { TransformContext } from '../transform'
|
||||||
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
import { isSlotOutlet } from '../utils'
|
||||||
|
|
||||||
export function hoistStatic(root: RootNode, context: TransformContext) {
|
export function hoistStatic(root: RootNode, context: TransformContext) {
|
||||||
walk(root.children, context, new Map<TemplateChildNode, boolean>())
|
walk(
|
||||||
|
root.children,
|
||||||
|
context,
|
||||||
|
new Map(),
|
||||||
|
isSingleElementRoot(root, root.children[0])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSingleElementRoot(
|
||||||
|
root: RootNode,
|
||||||
|
child: TemplateChildNode
|
||||||
|
): child is PlainElementNode | ComponentNode | TemplateNode {
|
||||||
|
const { children } = root
|
||||||
|
return (
|
||||||
|
children.length === 1 &&
|
||||||
|
child.type === NodeTypes.ELEMENT &&
|
||||||
|
!isSlotOutlet(child)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function walk(
|
function walk(
|
||||||
children: TemplateChildNode[],
|
children: TemplateChildNode[],
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
resultCache: Map<TemplateChildNode, boolean>
|
resultCache: Map<TemplateChildNode, boolean>,
|
||||||
|
doNotHoistNode: boolean = false
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
const child = children[i]
|
const child = children[i]
|
||||||
@ -27,7 +49,7 @@ function walk(
|
|||||||
child.type === NodeTypes.ELEMENT &&
|
child.type === NodeTypes.ELEMENT &&
|
||||||
child.tagType === ElementTypes.ELEMENT
|
child.tagType === ElementTypes.ELEMENT
|
||||||
) {
|
) {
|
||||||
if (isStaticNode(child, resultCache)) {
|
if (!doNotHoistNode && isStaticNode(child, resultCache)) {
|
||||||
// whole tree is static
|
// whole tree is static
|
||||||
;(child as any).codegenNode = context.hoist(child.codegenNode!)
|
;(child as any).codegenNode = context.hoist(child.codegenNode!)
|
||||||
continue
|
continue
|
||||||
@ -51,11 +73,16 @@ function walk(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (child.type === NodeTypes.ELEMENT || child.type === NodeTypes.FOR) {
|
if (child.type === NodeTypes.ELEMENT) {
|
||||||
walk(child.children, context, resultCache)
|
walk(child.children, context, resultCache)
|
||||||
|
} else if (child.type === NodeTypes.FOR) {
|
||||||
|
// Do not hoist v-for single child because it has to be a block
|
||||||
|
walk(child.children, context, resultCache, child.children.length === 1)
|
||||||
} else if (child.type === NodeTypes.IF) {
|
} else if (child.type === NodeTypes.IF) {
|
||||||
for (let i = 0; i < child.branches.length; i++) {
|
for (let i = 0; i < child.branches.length; i++) {
|
||||||
walk(child.branches[i].children, context, resultCache)
|
const branchChildren = child.branches[i].children
|
||||||
|
// Do not hoist v-if single child because it has to be a block
|
||||||
|
walk(branchChildren, context, resultCache, branchChildren.length === 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user