refactor(compiler-vapor): dynamicFlag

This commit is contained in:
三咲智子 Kevin Deng 2024-01-29 22:08:57 +08:00
parent 63a127b612
commit 79636ddc5b
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
8 changed files with 97 additions and 72 deletions

View File

@ -21,14 +21,15 @@ export function render(_ctx) {
`;
exports[`compiler: v-if > comment between branches 1`] = `
"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
"import { template as _template, children as _children, createIf as _createIf, prepend as _prepend, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const t1 = _template("<!--foo--><p></p>")
const t2 = _template("<!--bar-->fine")
const t3 = _fragment()
const t3 = _template("<input>")
const n0 = t3()
const { 0: [n5],} = _children(n0)
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
@ -40,6 +41,9 @@ export function render(_ctx) {
return n4
}))
_prepend(n0, n1)
_renderEffect(() => {
_setText(n5, _ctx.text)
})
return n0
}"
`;

View File

@ -1,5 +1,10 @@
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
import { IRNodeTypes, transformElement, transformVBind } from '../../src'
import {
DynamicFlag,
IRNodeTypes,
transformElement,
transformVBind,
} from '../../src'
import { makeCompile } from './_utils'
const compileWithVBind = makeCompile({
@ -15,7 +20,7 @@ describe('compiler v-bind', () => {
expect(ir.dynamic.children[0]).toMatchObject({
id: 1,
referenced: true,
dynamicFlags: DynamicFlag.REFERENCED,
})
expect(ir.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY,

View File

@ -276,6 +276,7 @@ describe('compiler: v-if', () => {
<p v-else-if="orNot"/>
<!--bar-->
<template v-else>fine</template>
<input v-text="text" />
`)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(4)
@ -292,7 +293,10 @@ describe('compiler: v-if', () => {
template: '<!--bar-->fine',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{ type: IRNodeTypes.FRAGMENT_FACTORY },
{
type: IRNodeTypes.TEMPLATE_FACTORY,
template: '<input>',
},
])
})

View File

@ -9,6 +9,7 @@ import {
} from '@vue/compiler-dom'
import {
type BlockFunctionIRNode,
DynamicFlag,
type IRDynamicChildren,
IRNodeTypes,
type OperationNode,
@ -317,20 +318,27 @@ function genChildren(children: IRDynamicChildren) {
let offset = 0
for (const [index, child] of Object.entries(children)) {
const childrenLength = Object.keys(child.children).length
if (child.ghost && child.placeholder === null && childrenLength === 0) {
if (
child.dynamicFlags & DynamicFlag.NON_TEMPLATE ||
(child.dynamicFlags & DynamicFlag.INSERT &&
child.placeholder === null &&
childrenLength === 0)
) {
offset--
continue
}
code += ` ${Number(index) + offset}: [`
const id = child.ghost ? child.placeholder : child.id
if (id !== null) code += `n${id}`
const idx = Number(index) + offset
const id =
child.dynamicFlags & DynamicFlag.INSERT ? child.placeholder : child.id
const childrenString = childrenLength && genChildren(child.children)
if (childrenString) code += `, ${childrenString}`
code += '],'
if (id !== null || childrenString) {
code += ` ${idx}: [`
if (id !== null) code += `n${id}`
if (childrenString) code += `, ${childrenString}`
code += '],'
}
}
if (!code) return ''

View File

@ -180,11 +180,25 @@ export type OperationNode =
export type BlockIRNode = RootIRNode | BlockFunctionIRNode
export enum DynamicFlag {
NONE = 0,
/**
* This node is referenced and needs to be saved as a variable.
*/
REFERENCED = 1,
/**
* This node is not generated from template, but is generated dynamically.
*/
NON_TEMPLATE = 1 << 1,
/**
* This node needs to be inserted back into the template.
*/
INSERT = 1 << 2,
}
export interface IRDynamicInfo {
id: number | null
referenced: boolean
/** created by DOM API */
ghost: boolean
dynamicFlags: DynamicFlag
placeholder: number | null
children: IRDynamicChildren
}

View File

@ -14,18 +14,17 @@ import {
} from '@vue/compiler-dom'
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
import {
type BlockIRNode,
DynamicFlag,
type FragmentFactoryIRNode,
type HackOptions,
type IRDynamicInfo,
type IRExpression,
IRNodeTypes,
type OperationNode,
type RootIRNode,
} from './ir'
import type {
BlockIRNode,
FragmentFactoryIRNode,
HackOptions,
TemplateFactoryIRNode,
VaporDirectiveNode,
type TemplateFactoryIRNode,
type VaporDirectiveNode,
} from './ir'
export type NodeTransform = (
@ -100,6 +99,13 @@ const defaultOptions = {
onWarn: defaultOnWarn,
}
export const genDefaultDynamic = (): IRDynamicInfo => ({
id: null,
dynamicFlags: 0,
placeholder: null,
children: {},
})
// TODO use class for better perf
function createRootContext(
root: RootIRNode,
@ -135,7 +141,7 @@ function createRootContext(
increaseId: () => globalId++,
reference() {
if (this.dynamic.id !== null) return this.dynamic.id
this.dynamic.referenced = true
this.dynamic.dynamicFlags |= DynamicFlag.REFERENCED
return (this.dynamic.id = this.increaseId())
},
registerEffect(expressions, operations) {
@ -220,14 +226,8 @@ function createContext<T extends TemplateChildNode>(
template: '',
childrenTemplate: [],
dynamic: {
id: null,
referenced: false,
ghost: false,
placeholder: null,
children: {},
},
})
dynamic: genDefaultDynamic(),
} satisfies Partial<TransformContext<T>>)
return ctx
}
@ -243,13 +243,9 @@ export function transform(
loc: root.loc,
template: [],
templateIndex: -1,
dynamic: {
id: null,
referenced: true,
ghost: true,
placeholder: null,
children: {},
},
dynamic: extend(genDefaultDynamic(), {
dynamicFlags: DynamicFlag.REFERENCED | DynamicFlag.INSERT,
} satisfies Partial<IRDynamicInfo>),
effect: [],
operation: [],
}
@ -287,6 +283,7 @@ function transformNode(
node = context.node
}
}
switch (node.type) {
case NodeTypes.ROOT:
case NodeTypes.ELEMENT: {
@ -322,14 +319,7 @@ function transformChildren(ctx: TransformContext<RootNode | ElementNode>) {
const childContext = createContext(child, ctx, i)
transformNode(childContext)
ctx.childrenTemplate.push(childContext.template)
if (
childContext.dynamic.ghost ||
childContext.dynamic.referenced ||
childContext.dynamic.placeholder ||
Object.keys(childContext.dynamic.children).length
) {
ctx.dynamic.children[i] = childContext.dynamic
}
ctx.dynamic.children[i] = childContext.dynamic
}
processDynamicChildren(ctx)
@ -344,7 +334,7 @@ function processDynamicChildren(ctx: TransformContext<RootNode | ElementNode>) {
for (let index = 0; index < node.children.length; index++) {
const child = ctx.dynamic.children[index]
if (!child || !child.ghost) {
if (!child || !(child.dynamicFlags & DynamicFlag.INSERT)) {
if (prevChildren.length) {
if (hasStatic) {
ctx.childrenTemplate[index - prevChildren.length] = `<!>`

View File

@ -1,6 +1,6 @@
import { NodeTypes, type SimpleExpressionNode } from '@vue/compiler-dom'
import type { NodeTransform } from '../transform'
import { IRNodeTypes } from '../ir'
import { DynamicFlag, IRNodeTypes } from '../ir'
export const transformInterpolation: NodeTransform = (node, ctx) => {
if (node.type !== NodeTypes.INTERPOLATION) return
@ -27,7 +27,7 @@ export const transformInterpolation: NodeTransform = (node, ctx) => {
)
} else {
const id = ctx.reference()
ctx.dynamic.ghost = true
ctx.dynamic.dynamicFlags |= DynamicFlag.INSERT
ctx.registerOperation({
type: IRNodeTypes.CREATE_TEXT_NODE,
loc: node.loc,

View File

@ -12,9 +12,12 @@ import {
import {
type TransformContext,
createStructuralDirectiveTransform,
genDefaultDynamic,
} from '../transform'
import {
type BlockFunctionIRNode,
DynamicFlag,
type IRDynamicInfo,
IRNodeTypes,
type OperationNode,
type VaporDirectiveNode,
@ -41,7 +44,7 @@ export function processIf(
if (dir.name === 'if') {
const id = context.reference()
context.dynamic.ghost = true
context.dynamic.dynamicFlags |= DynamicFlag.INSERT
const [branch, onExit] = createIfBranch(node, context)
return () => {
@ -55,10 +58,13 @@ export function processIf(
})
}
} else {
context.dynamic.dynamicFlags |= DynamicFlag.NON_TEMPLATE
// check the adjacent v-if
const parent = context.parent!
const siblings = parent.node.children
const templates = parent.childrenTemplate
const siblingsDynamic = parent.dynamic.children
const comments = []
let sibling: TemplateChildNode | undefined
@ -66,20 +72,18 @@ export function processIf(
while (i-- >= -1) {
sibling = siblings[i]
if (sibling) {
if (sibling.type === NodeTypes.COMMENT) {
__DEV__ && comments.unshift(sibling)
templates[i] = null
continue
} else if (
sibling.type === NodeTypes.TEXT &&
!sibling.content.trim().length
) {
templates[i] = null
continue
}
if (
sibling &&
(sibling.type === NodeTypes.COMMENT ||
(sibling.type === NodeTypes.TEXT && !sibling.content.trim().length))
) {
if (__DEV__ && sibling.type === NodeTypes.COMMENT)
comments.unshift(sibling)
siblingsDynamic[i].dynamicFlags |= DynamicFlag.NON_TEMPLATE
templates[i] = null
} else {
break
}
break
}
const { operation } = context.block
@ -150,13 +154,9 @@ export function createIfBranch(
loc: node.loc,
node,
templateIndex: -1,
dynamic: {
id: null,
referenced: true,
ghost: true,
placeholder: null,
children: {},
},
dynamic: extend(genDefaultDynamic(), {
dynamicFlags: DynamicFlag.REFERENCED | DynamicFlag.INSERT,
} satisfies Partial<IRDynamicInfo>),
effect: [],
operation: [],
}
@ -164,7 +164,7 @@ export function createIfBranch(
const exitBlock = context.enterBlock(branch)
context.reference()
const onExit = () => {
context.template += context.childrenTemplate.join('')
context.template += context.childrenTemplate.filter(Boolean).join('')
context.registerTemplate()
exitBlock()
}