wip(compiler): tests for new stringification

This commit is contained in:
Evan You
2020-05-15 15:50:42 -04:00
parent dbf627f136
commit baa6973b13
5 changed files with 88 additions and 55 deletions

View File

@@ -31,7 +31,7 @@ describe('stringify static html', () => {
)
expect(ast.hoists.length).toBe(1)
// should be a normal vnode call
expect(ast.hoists[0].type).toBe(NodeTypes.VNODE_CALL)
expect(ast.hoists[0]!.type).toBe(NodeTypes.VNODE_CALL)
})
test('should work on eligible content (elements with binding > 5)', () => {
@@ -52,7 +52,8 @@ describe('stringify static html', () => {
`<span class="foo"></span>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</div>`
)
),
'1'
]
})
})
@@ -75,7 +76,36 @@ describe('stringify static html', () => {
`<span></span>`,
StringifyThresholds.NODE_COUNT
)}</div>`
)
),
'1'
]
})
})
test('should work for multiple adjacent nodes', () => {
const { ast } = compileWithStringify(
`<div>${repeat(
`<span class="foo"/>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</div>`
)
// should have 5 hoisted nodes, but the other 4 should be null
expect(ast.hoists.length).toBe(5)
for (let i = 1; i < 5; i++) {
expect(ast.hoists[i]).toBe(null)
}
// should be optimized now
expect(ast.hoists[0]).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_STATIC,
arguments: [
JSON.stringify(
repeat(
`<span class="foo"></span>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)
),
'5'
]
})
})
@@ -98,7 +128,8 @@ describe('stringify static html', () => {
`<span class="foo bar">1 + false</span>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</div>`
)
),
'1'
]
})
})
@@ -122,7 +153,8 @@ describe('stringify static html', () => {
`<span class="foo&gt;ar">1 + &lt;</span>` + `<span>&amp;</span>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</div>`
)
),
'1'
]
})
})

View File

@@ -13,8 +13,7 @@ import {
ExpressionNode,
ElementTypes,
PlainElementNode,
JSChildNode,
createSimpleExpression
JSChildNode
} from '@vue/compiler-core'
import {
isVoidTag,
@@ -39,9 +38,42 @@ export const enum StringifyThresholds {
export const stringifyStatic: HoistTransform = (children, context) => {
let nc = 0 // current node count
let ec = 0 // current element with binding count
const currentEligibleNodes: PlainElementNode[] = []
const currentChunk: PlainElementNode[] = []
for (let i = 0; i < children.length; i++) {
const stringifyCurrentChunk = (currentIndex: number): number => {
if (
nc >= StringifyThresholds.NODE_COUNT ||
ec >= StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
) {
// combine all currently eligible nodes into a single static vnode call
const staticCall = createCallExpression(context.helper(CREATE_STATIC), [
JSON.stringify(
currentChunk.map(node => stringifyElement(node, context)).join('')
),
// the 2nd argument indicates the number of DOM nodes this static vnode
// will insert / hydrate
String(currentChunk.length)
])
// replace the first node's hoisted expression with the static vnode call
replaceHoist(currentChunk[0], staticCall, context)
if (currentChunk.length > 1) {
for (let i = 1; i < currentChunk.length; i++) {
// for the merged nodes, set their hoisted expression to null
replaceHoist(currentChunk[i], null, context)
}
// also remove merged nodes from children
const deleteCount = currentChunk.length - 1
children.splice(currentIndex - currentChunk.length + 1, deleteCount)
return deleteCount
}
}
return 0
}
let i = 0
for (; i < children.length; i++) {
const child = children[i]
const hoisted = getHoistedNode(child)
if (hoisted) {
@@ -52,54 +84,21 @@ export const stringifyStatic: HoistTransform = (children, context) => {
// node is stringifiable, record state
nc += result[0]
ec += result[1]
currentEligibleNodes.push(node)
currentChunk.push(node)
continue
}
}
// we only reach here if we ran into a node that is not stringifiable
// check if currently analyzed nodes meet criteria for stringification.
if (
nc >= StringifyThresholds.NODE_COUNT ||
ec >= StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
) {
// combine all currently eligible nodes into a single static vnode call
const staticCall = createCallExpression(context.helper(CREATE_STATIC), [
JSON.stringify(
currentEligibleNodes
.map(node => stringifyElement(node, context))
.join('')
),
// the 2nd argument indicates the number of DOM nodes this static vnode
// will insert / hydrate
String(currentEligibleNodes.length)
])
// replace the first node's hoisted expression with the static vnode call
replaceHoist(currentEligibleNodes[0], staticCall, context)
const n = currentEligibleNodes.length
if (n > 1) {
for (let j = 1; j < n; j++) {
// for the merged nodes, set their hoisted expression to null
replaceHoist(
currentEligibleNodes[j],
createSimpleExpression(`null`, false),
context
)
}
// also remove merged nodes from children
const deleteCount = n - 1
children.splice(i - n + 1, deleteCount)
// adjust iteration index
i -= deleteCount
}
}
// adjust iteration index
i -= stringifyCurrentChunk(i)
// reset state
nc = 0
ec = 0
currentEligibleNodes.length = 0
currentChunk.length = 0
}
// in case the last node was also stringifiable
stringifyCurrentChunk(i)
}
const getHoistedNode = (node: TemplateChildNode) =>
@@ -116,7 +115,7 @@ const isStringifiableAttr = (name: string) => {
const replaceHoist = (
node: PlainElementNode,
replacement: JSChildNode,
replacement: JSChildNode | null,
context: TransformContext
) => {
const hoistToReplace = (node.codegenNode as SimpleExpressionNode).hoisted!