mirror of https://github.com/vuejs/core.git
refactor(compiler-vapor): dynamicFlag
This commit is contained in:
parent
63a127b612
commit
79636ddc5b
|
|
@ -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
|
||||
}"
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ''
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] = `<!>`
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue