mirror of https://github.com/vuejs/core.git
Merge branch 'vapor' into edison/feat/vaporTransition
This commit is contained in:
commit
7c9bd7c093
|
@ -149,17 +149,17 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
|||
`;
|
||||
|
||||
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, child as _child, createComponentWithFallback as _createComponentWithFallback, prepend as _prepend, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
|
||||
const t1 = _template("<div></div>")
|
||||
const t1 = _template("<div> </div>")
|
||||
|
||||
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n0 = t0()
|
||||
const n3 = t1()
|
||||
const n2 = _child(n3)
|
||||
const n1 = _createComponentWithFallback(_component_Comp)
|
||||
const n2 = _createTextNode()
|
||||
_insert([n1, n2], n3)
|
||||
_prepend(n3, n1)
|
||||
_renderEffect(() => {
|
||||
_setText(n2, _toDisplayString(_ctx.bar))
|
||||
_setProp(n3, "id", _ctx.foo)
|
||||
|
@ -169,10 +169,12 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
|||
`;
|
||||
|
||||
exports[`compile > dynamic root 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2))
|
||||
const n0 = t0()
|
||||
_setText(n0, _toDisplayString(1) + _toDisplayString(2))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
@ -197,7 +199,7 @@ export function render(_ctx) {
|
|||
|
||||
exports[`compile > expression parsing > interpolation 1`] = `
|
||||
"
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(a + b.value)))
|
||||
return n0
|
||||
"
|
||||
|
@ -229,10 +231,12 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compile > static + dynamic root 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2) + "3" + _toDisplayString(4) + _toDisplayString(5) + "6" + _toDisplayString(7) + _toDisplayString(8) + "9" + 'A' + 'B')
|
||||
const n0 = t0()
|
||||
_setText(n0, _toDisplayString(1) + _toDisplayString(2) + "3" + _toDisplayString(4) + _toDisplayString(5) + "6" + _toDisplayString(7) + _toDisplayString(8) + "9" + 'A' + 'B')
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: expression > basic 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_ctx.a)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > props 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString($props.foo)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > props aliased 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString($props['bar'])))
|
||||
return n0
|
||||
}"
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: children transform > children & sibling references 1`] = `
|
||||
"import { child as _child, nthChild as _nthChild, next as _next, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div><p> </p> <!><p> </p></div>", true)
|
||||
"import { child as _child, next as _next, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div><p> </p> <p> </p></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const n4 = t0()
|
||||
const n0 = _child(n4)
|
||||
const n3 = _nthChild(n4, 2)
|
||||
const n2 = _next(n3)
|
||||
const n3 = t0()
|
||||
const n0 = _child(n3)
|
||||
const n1 = _next(n0)
|
||||
const n2 = _next(n1)
|
||||
const x0 = _child(n0)
|
||||
const n1 = _createTextNode()
|
||||
const x2 = _child(n2)
|
||||
_insert(n1, n4, n3)
|
||||
_renderEffect(() => {
|
||||
_setText(x0, _toDisplayString(_ctx.first))
|
||||
_setText(n1, _toDisplayString(_ctx.second) + " " + _toDisplayString(_ctx.third) + " ")
|
||||
_setText(n1, " " + _toDisplayString(_ctx.second) + " " + _toDisplayString(_ctx.third) + " ")
|
||||
_setText(x2, _toDisplayString(_ctx.forth))
|
||||
})
|
||||
return n4
|
||||
return n3
|
||||
}"
|
||||
`;
|
||||
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: text transform > consecutive text 1`] = `
|
||||
"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_ctx.msg)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: text transform > no consecutive text 1`] = `
|
||||
"import { createTextNode as _createTextNode } from 'vue';
|
||||
"import { setText as _setText, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = _createTextNode("hello world")
|
||||
const n0 = t0()
|
||||
_setText(n0, "hello world")
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -12,15 +12,15 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: v-once > basic 1`] = `
|
||||
"import { child as _child, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setClass as _setClass, prepend as _prepend, template as _template } from 'vue';
|
||||
const t0 = _template("<div><span></span></div>", true)
|
||||
"import { child as _child, next as _next, toDisplayString as _toDisplayString, setText as _setText, setClass as _setClass, template as _template } from 'vue';
|
||||
const t0 = _template("<div> <span></span></div>", true)
|
||||
|
||||
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||
const n2 = t0()
|
||||
const n1 = _child(n2)
|
||||
const n0 = _createTextNode(_toDisplayString(_ctx.msg) + " ")
|
||||
const n0 = _child(n2)
|
||||
const n1 = _next(n0)
|
||||
_setText(n0, _toDisplayString(_ctx.msg) + " ")
|
||||
_setClass(n1, _ctx.clz)
|
||||
_prepend(n2, n0)
|
||||
return n2
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -22,7 +22,8 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
@ -31,7 +32,7 @@ export function render(_ctx) {
|
|||
() => (_createForSlots(_ctx.list, (item) => ({
|
||||
name: item,
|
||||
fn: (_slotProps0) => {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"])))
|
||||
return n0
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: transform slot > nested slots scoping 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
|
@ -166,17 +167,16 @@ export function render(_ctx) {
|
|||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n5 = _createComponentWithFallback(_component_Comp, null, {
|
||||
"default": (_slotProps0) => {
|
||||
const n2 = t0()
|
||||
const n3 = t0()
|
||||
const n1 = _createComponentWithFallback(_component_Inner, null, {
|
||||
"default": (_slotProps1) => {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz)))
|
||||
return n0
|
||||
}
|
||||
})
|
||||
const n3 = _createTextNode()
|
||||
_renderEffect(() => _setText(n3, _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
|
||||
return [n1, n2, n3]
|
||||
_renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
|
||||
return [n1, n3]
|
||||
}
|
||||
}, true)
|
||||
return n5
|
||||
|
@ -184,7 +184,8 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: transform slot > on component dynamically named slot 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
@ -193,7 +194,7 @@ export function render(_ctx) {
|
|||
() => ({
|
||||
name: _ctx.named,
|
||||
fn: (_slotProps0) => {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
|
||||
return n0
|
||||
}
|
||||
|
@ -205,13 +206,14 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: transform slot > on component named slot 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n1 = _createComponentWithFallback(_component_Comp, null, {
|
||||
"named": (_slotProps0) => {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
|
||||
return n0
|
||||
}
|
||||
|
@ -221,13 +223,14 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: transform slot > on-component default slot 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n1 = _createComponentWithFallback(_component_Comp, null, {
|
||||
"default": (_slotProps0) => {
|
||||
const n0 = _createTextNode()
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
|
||||
return n0
|
||||
}
|
||||
|
|
|
@ -27,10 +27,11 @@ describe('compiler: children transform', () => {
|
|||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(Array.from(helpers)).containSubset([
|
||||
'child',
|
||||
'toDisplayString',
|
||||
'renderEffect',
|
||||
'next',
|
||||
'setText',
|
||||
'createTextNode',
|
||||
'insert',
|
||||
'template',
|
||||
])
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// TODO: add tests for this transform
|
||||
import { NodeTypes } from '@vue/compiler-dom'
|
||||
import {
|
||||
IRNodeTypes,
|
||||
transformChildren,
|
||||
|
@ -24,14 +25,14 @@ describe('compiler: text transform', () => {
|
|||
'{{ "hello world" }}',
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(helpers).contains.all.keys('createTextNode')
|
||||
expect(helpers).contains.all.keys('setText', 'template')
|
||||
expect(ir.block.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.CREATE_TEXT_NODE,
|
||||
id: 0,
|
||||
type: IRNodeTypes.SET_TEXT,
|
||||
element: 0,
|
||||
values: [
|
||||
{
|
||||
type: IRNodeTypes.SET_TEXT,
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: '"hello world"',
|
||||
isStatic: false,
|
||||
},
|
||||
|
@ -43,14 +44,8 @@ describe('compiler: text transform', () => {
|
|||
it('consecutive text', () => {
|
||||
const { code, ir, helpers } = compileWithTextTransform('{{ msg }}')
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(helpers).contains.all.keys('createTextNode')
|
||||
expect(ir.block.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.CREATE_TEXT_NODE,
|
||||
id: 0,
|
||||
values: undefined,
|
||||
},
|
||||
])
|
||||
expect(helpers).contains.all.keys('setText', 'template')
|
||||
expect(ir.block.operation).toMatchObject([])
|
||||
expect(ir.block.effect.length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -28,8 +28,8 @@ describe('compiler: v-once', () => {
|
|||
expect(ir.block.effect).lengthOf(0)
|
||||
expect(ir.block.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.CREATE_TEXT_NODE,
|
||||
id: 0,
|
||||
type: IRNodeTypes.SET_TEXT,
|
||||
element: 0,
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
|
@ -61,11 +61,6 @@ describe('compiler: v-once', () => {
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: IRNodeTypes.PREPEND_NODE,
|
||||
elements: [0],
|
||||
parent: 2,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
|
|
|
@ -66,11 +66,10 @@ export function genChildren(
|
|||
push(NEWLINE, `const ${variable} = `)
|
||||
|
||||
if (prev) {
|
||||
const offset = elementIndex - prev[1]
|
||||
if (offset === 1) {
|
||||
if (elementIndex - prev[1] === 1) {
|
||||
push(...genCall(helper('next'), prev[0]))
|
||||
} else {
|
||||
push(...genCall(helper('nthChild'), from, String(offset)))
|
||||
push(...genCall(helper('nthChild'), from, String(elementIndex)))
|
||||
}
|
||||
} else {
|
||||
if (newPath.length === 1 && newPath[0] === 0) {
|
||||
|
|
|
@ -30,32 +30,71 @@ export const transformText: NodeTransform = (node, context) => {
|
|||
return
|
||||
}
|
||||
|
||||
const isFragment =
|
||||
node.type === NodeTypes.ROOT ||
|
||||
(node.type === NodeTypes.ELEMENT &&
|
||||
(node.tagType === ElementTypes.TEMPLATE ||
|
||||
node.tagType === ElementTypes.COMPONENT))
|
||||
|
||||
if (
|
||||
node.type === NodeTypes.ELEMENT &&
|
||||
node.tagType === ElementTypes.ELEMENT &&
|
||||
isAllTextLike(node.children)
|
||||
(isFragment ||
|
||||
(node.type === NodeTypes.ELEMENT &&
|
||||
node.tagType === ElementTypes.ELEMENT)) &&
|
||||
node.children.length
|
||||
) {
|
||||
processTextLikeContainer(
|
||||
node.children,
|
||||
context as TransformContext<ElementNode>,
|
||||
)
|
||||
let hasInterp = false
|
||||
let isAllTextLike = true
|
||||
for (const c of node.children) {
|
||||
if (c.type === NodeTypes.INTERPOLATION) {
|
||||
hasInterp = true
|
||||
} else if (c.type !== NodeTypes.TEXT) {
|
||||
isAllTextLike = false
|
||||
}
|
||||
}
|
||||
// all text like with interpolation
|
||||
if (!isFragment && isAllTextLike && hasInterp) {
|
||||
processTextContainer(
|
||||
node.children as TextLike[],
|
||||
context as TransformContext<ElementNode>,
|
||||
)
|
||||
} else if (hasInterp) {
|
||||
// check if there's any text before interpolation, it needs to be merged
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
const c = node.children[i]
|
||||
const prev = node.children[i - 1]
|
||||
if (
|
||||
c.type === NodeTypes.INTERPOLATION &&
|
||||
prev &&
|
||||
prev.type === NodeTypes.TEXT
|
||||
) {
|
||||
// mark leading text node for skipping
|
||||
seen.get(context.root)!.add(prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (node.type === NodeTypes.INTERPOLATION) {
|
||||
processTextLike(context as TransformContext<InterpolationNode>)
|
||||
processInterpolation(context as TransformContext<InterpolationNode>)
|
||||
} else if (node.type === NodeTypes.TEXT) {
|
||||
context.template += node.content
|
||||
}
|
||||
}
|
||||
|
||||
function processTextLike(context: TransformContext<InterpolationNode>) {
|
||||
const nexts = context.parent!.node.children.slice(context.index)
|
||||
function processInterpolation(context: TransformContext<InterpolationNode>) {
|
||||
const children = context.parent!.node.children
|
||||
const nexts = children.slice(context.index)
|
||||
const idx = nexts.findIndex(n => !isTextLike(n))
|
||||
const nodes = (idx > -1 ? nexts.slice(0, idx) : nexts) as Array<TextLike>
|
||||
|
||||
// merge leading text
|
||||
const prev = children[context.index - 1]
|
||||
if (prev && prev.type === NodeTypes.TEXT) {
|
||||
nodes.unshift(prev)
|
||||
}
|
||||
|
||||
context.template += ' '
|
||||
const id = context.reference()
|
||||
const values = nodes.map(node => createTextLikeExpression(node, context))
|
||||
|
||||
context.dynamic.flags |= DynamicFlag.INSERT | DynamicFlag.NON_TEMPLATE
|
||||
|
||||
const nonConstantExps = values.filter(v => !isConstantExpression(v))
|
||||
const isStatic =
|
||||
!nonConstantExps.length ||
|
||||
|
@ -64,12 +103,13 @@ function processTextLike(context: TransformContext<InterpolationNode>) {
|
|||
) ||
|
||||
context.inVOnce
|
||||
|
||||
context.registerOperation({
|
||||
type: IRNodeTypes.CREATE_TEXT_NODE,
|
||||
id,
|
||||
values: isStatic ? values : undefined,
|
||||
})
|
||||
if (!isStatic) {
|
||||
if (isStatic) {
|
||||
context.registerOperation({
|
||||
type: IRNodeTypes.SET_TEXT,
|
||||
element: id,
|
||||
values,
|
||||
})
|
||||
} else {
|
||||
context.registerEffect(values, {
|
||||
type: IRNodeTypes.SET_TEXT,
|
||||
element: id,
|
||||
|
@ -78,7 +118,7 @@ function processTextLike(context: TransformContext<InterpolationNode>) {
|
|||
}
|
||||
}
|
||||
|
||||
function processTextLikeContainer(
|
||||
function processTextContainer(
|
||||
children: TextLike[],
|
||||
context: TransformContext<ElementNode>,
|
||||
) {
|
||||
|
@ -111,15 +151,6 @@ function createTextLikeExpression(node: TextLike, context: TransformContext) {
|
|||
}
|
||||
}
|
||||
|
||||
function isAllTextLike(children: TemplateChildNode[]): children is TextLike[] {
|
||||
return (
|
||||
!!children.length &&
|
||||
children.every(isTextLike) &&
|
||||
// at least one an interpolation
|
||||
children.some(n => n.type === NodeTypes.INTERPOLATION)
|
||||
)
|
||||
}
|
||||
|
||||
function isTextLike(node: TemplateChildNode): node is TextLike {
|
||||
return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT
|
||||
}
|
||||
|
|
|
@ -35,8 +35,10 @@ describe('api: createVaporApp', () => {
|
|||
const root1 = document.createElement('div')
|
||||
createVaporApp(Comp).mount(root1)
|
||||
expect(root1.innerHTML).toBe(`0`)
|
||||
|
||||
//#5571 mount multiple apps to the same host element
|
||||
createVaporApp(Comp).mount(root1)
|
||||
expect(`mount target container is not empty`).toHaveBeenWarned()
|
||||
expect(
|
||||
`There is already an app instance mounted on the host container`,
|
||||
).toHaveBeenWarned()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,7 @@ import {
|
|||
unmountComponent,
|
||||
} from './component'
|
||||
import {
|
||||
type App,
|
||||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
type CreateAppFunction,
|
||||
|
@ -20,6 +21,7 @@ import {
|
|||
import type { RawProps } from './componentProps'
|
||||
import { getGlobalThis } from '@vue/shared'
|
||||
import { optimizePropertyLookup } from './dom/prop'
|
||||
import { withHydration } from './dom/hydration'
|
||||
|
||||
let _createApp: CreateAppFunction<ParentNode, VaporComponent>
|
||||
|
||||
|
@ -28,6 +30,9 @@ const mountApp: AppMountFn<ParentNode> = (app, container) => {
|
|||
|
||||
// clear content before mounting
|
||||
if (container.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
||||
if (__DEV__ && container.childNodes.length) {
|
||||
warn('mount target container is not empty and will be cleared.')
|
||||
}
|
||||
container.textContent = ''
|
||||
}
|
||||
|
||||
|
@ -38,21 +43,38 @@ const mountApp: AppMountFn<ParentNode> = (app, container) => {
|
|||
false,
|
||||
app._context,
|
||||
)
|
||||
|
||||
mountComponent(instance, container)
|
||||
flushOnAppMount()
|
||||
|
||||
return instance
|
||||
return instance!
|
||||
}
|
||||
|
||||
let _hydrateApp: CreateAppFunction<ParentNode, VaporComponent>
|
||||
|
||||
const hydrateApp: AppMountFn<ParentNode> = (app, container) => {
|
||||
optimizePropertyLookup()
|
||||
|
||||
let instance: VaporComponentInstance
|
||||
withHydration(container, () => {
|
||||
instance = createComponent(
|
||||
app._component,
|
||||
app._props as RawProps,
|
||||
null,
|
||||
false,
|
||||
app._context,
|
||||
)
|
||||
mountComponent(instance, container)
|
||||
flushOnAppMount()
|
||||
})
|
||||
|
||||
return instance!
|
||||
}
|
||||
|
||||
const unmountApp: AppUnmountFn = app => {
|
||||
unmountComponent(app._instance as VaporComponentInstance, app._container)
|
||||
}
|
||||
|
||||
export const createVaporApp: CreateAppFunction<ParentNode, VaporComponent> = (
|
||||
comp,
|
||||
props,
|
||||
) => {
|
||||
function prepareApp() {
|
||||
// compile-time feature flags check
|
||||
if (__ESM_BUNDLER__ && !__TEST__) {
|
||||
initFeatureFlags()
|
||||
|
@ -63,10 +85,9 @@ export const createVaporApp: CreateAppFunction<ParentNode, VaporComponent> = (
|
|||
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||
setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target)
|
||||
}
|
||||
}
|
||||
|
||||
if (!_createApp) _createApp = createAppAPI(mountApp, unmountApp, getExposed)
|
||||
const app = _createApp(comp, props)
|
||||
|
||||
function postPrepareApp(app: App) {
|
||||
if (__DEV__) {
|
||||
app.config.globalProperties = new Proxy(
|
||||
{},
|
||||
|
@ -84,5 +105,27 @@ export const createVaporApp: CreateAppFunction<ParentNode, VaporComponent> = (
|
|||
container = normalizeContainer(container) as ParentNode
|
||||
return mount(container, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
export const createVaporApp: CreateAppFunction<ParentNode, VaporComponent> = (
|
||||
comp,
|
||||
props,
|
||||
) => {
|
||||
prepareApp()
|
||||
if (!_createApp) _createApp = createAppAPI(mountApp, unmountApp, getExposed)
|
||||
const app = _createApp(comp, props)
|
||||
postPrepareApp(app)
|
||||
return app
|
||||
}
|
||||
|
||||
export const createVaporSSRApp: CreateAppFunction<
|
||||
ParentNode,
|
||||
VaporComponent
|
||||
> = (comp, props) => {
|
||||
prepareApp()
|
||||
if (!_hydrateApp)
|
||||
_hydrateApp = createAppAPI(hydrateApp, unmountApp, getExposed)
|
||||
const app = _hydrateApp(comp, props)
|
||||
postPrepareApp(app)
|
||||
return app
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from './component'
|
||||
import { createComment, createTextNode } from './dom/node'
|
||||
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||
import { isHydrating } from './dom/hydration'
|
||||
import {
|
||||
type TransitionHooks,
|
||||
type TransitionProps,
|
||||
|
@ -154,26 +155,33 @@ export function insert(
|
|||
): void {
|
||||
anchor = anchor === 0 ? parent.firstChild : anchor
|
||||
if (block instanceof Node) {
|
||||
// don't apply transition on text or comment nodes
|
||||
if ((block as TransitionBlock).$transition && block instanceof Element) {
|
||||
performTransitionEnter(
|
||||
block,
|
||||
(block as TransitionBlock).$transition as TransitionHooks,
|
||||
() => parent.insertBefore(block, anchor),
|
||||
parentSuspense,
|
||||
)
|
||||
} else {
|
||||
parent.insertBefore(block, anchor)
|
||||
if (!isHydrating) {
|
||||
// don't apply transition on text or comment nodes
|
||||
if ((block as TransitionBlock).$transition && block instanceof Element) {
|
||||
performTransitionEnter(
|
||||
block,
|
||||
(block as TransitionBlock).$transition as TransitionHooks,
|
||||
() => parent.insertBefore(block, anchor),
|
||||
parentSuspense,
|
||||
)
|
||||
} else {
|
||||
parent.insertBefore(block, anchor)
|
||||
}
|
||||
}
|
||||
} else if (isVaporComponent(block)) {
|
||||
mountComponent(block, parent, anchor)
|
||||
if (block.isMounted) {
|
||||
insert(block.block!, parent, anchor)
|
||||
} else {
|
||||
mountComponent(block, parent, anchor)
|
||||
}
|
||||
} else if (isArray(block)) {
|
||||
for (let i = 0; i < block.length; i++) {
|
||||
insert(block[i], parent, anchor)
|
||||
for (const b of block) {
|
||||
insert(b, parent, anchor)
|
||||
}
|
||||
} else {
|
||||
// fragment
|
||||
if (block.insert) {
|
||||
// TODO handle hydration for vdom interop
|
||||
block.insert(parent, anchor, (block as TransitionBlock).$transition)
|
||||
} else {
|
||||
insert(block.nodes, parent, anchor, parentSuspense)
|
||||
|
@ -182,6 +190,8 @@ export function insert(
|
|||
}
|
||||
}
|
||||
|
||||
export type InsertFn = typeof insert
|
||||
|
||||
export function prepend(parent: ParentNode, ...blocks: Block[]): void {
|
||||
let i = blocks.length
|
||||
while (i--) insert(blocks[i], parent, 0)
|
||||
|
|
|
@ -480,14 +480,10 @@ export function mountComponent(
|
|||
if (__DEV__) {
|
||||
startMeasure(instance, `mount`)
|
||||
}
|
||||
if (!instance.isMounted) {
|
||||
if (instance.bm) invokeArrayFns(instance.bm)
|
||||
insert(instance.block, parent, anchor)
|
||||
if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!))
|
||||
instance.isMounted = true
|
||||
} else {
|
||||
insert(instance.block, parent, anchor)
|
||||
}
|
||||
if (instance.bm) invokeArrayFns(instance.bm)
|
||||
insert(instance.block, parent, anchor)
|
||||
if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!))
|
||||
instance.isMounted = true
|
||||
if (__DEV__) {
|
||||
endMeasure(instance, `mount`)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
import { child, next } from './node'
|
||||
|
||||
export let isHydrating = false
|
||||
export let currentHydrationNode: Node | null = null
|
||||
|
||||
export function setCurrentHydrationNode(node: Node | null): void {
|
||||
currentHydrationNode = node
|
||||
}
|
||||
|
||||
let isOptimized = false
|
||||
|
||||
export function withHydration(container: ParentNode, fn: () => void): void {
|
||||
adoptHydrationNode = adoptHydrationNodeImpl
|
||||
if (!isOptimized) {
|
||||
// optimize anchor cache lookup
|
||||
const proto = Comment.prototype as any
|
||||
proto.$p = proto.$e = undefined
|
||||
isOptimized = true
|
||||
}
|
||||
isHydrating = true
|
||||
currentHydrationNode = child(container)
|
||||
const res = fn()
|
||||
isHydrating = false
|
||||
currentHydrationNode = null
|
||||
return res
|
||||
}
|
||||
|
||||
export let adoptHydrationNode: (
|
||||
node: Node | null,
|
||||
template?: string,
|
||||
) => Node | null
|
||||
|
||||
type Anchor = Comment & {
|
||||
// previous open anchor
|
||||
$p?: Anchor
|
||||
// matching end anchor
|
||||
$e?: Anchor
|
||||
}
|
||||
|
||||
const isComment = (node: Node, data: string): node is Anchor =>
|
||||
node.nodeType === 8 && (node as Comment).data === data
|
||||
|
||||
/**
|
||||
* Locate the first non-fragment-comment node and locate the next node
|
||||
* while handling potential fragments.
|
||||
*/
|
||||
function adoptHydrationNodeImpl(
|
||||
node: Node | null,
|
||||
template?: string,
|
||||
): Node | null {
|
||||
if (!isHydrating || !node) {
|
||||
return node
|
||||
}
|
||||
|
||||
let adopted: Node | undefined
|
||||
let end: Node | undefined | null
|
||||
|
||||
if (template) {
|
||||
if (template[0] !== '<' && template[1] !== '!') {
|
||||
while (node.nodeType === 8) node = next(node)
|
||||
}
|
||||
adopted = end = node
|
||||
} else if (isComment(node, '[')) {
|
||||
// fragment
|
||||
let start = node
|
||||
let cur: Node = node
|
||||
let fragmentDepth = 1
|
||||
// previously recorded fragment end
|
||||
if (!end && node.$e) {
|
||||
end = node.$e
|
||||
}
|
||||
while (true) {
|
||||
cur = next(cur)
|
||||
if (isComment(cur, '[')) {
|
||||
// previously recorded fragment end
|
||||
if (!end && node.$e) {
|
||||
end = node.$e
|
||||
}
|
||||
fragmentDepth++
|
||||
cur.$p = start
|
||||
start = cur
|
||||
} else if (isComment(cur, ']')) {
|
||||
fragmentDepth--
|
||||
// record fragment end on start node for later traversal
|
||||
start.$e = cur
|
||||
start = start.$p!
|
||||
if (!fragmentDepth) {
|
||||
// fragment end
|
||||
end = cur
|
||||
break
|
||||
}
|
||||
} else if (!adopted) {
|
||||
adopted = cur
|
||||
if (end) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!adopted) {
|
||||
throw new Error('hydration mismatch')
|
||||
}
|
||||
} else {
|
||||
adopted = end = node
|
||||
}
|
||||
|
||||
if (__DEV__ && template) {
|
||||
const type = adopted.nodeType
|
||||
if (
|
||||
(type === 8 && !template.startsWith('<!')) ||
|
||||
(type === 1 &&
|
||||
!template.startsWith(
|
||||
`<` + (adopted as Element).tagName.toLowerCase(),
|
||||
)) ||
|
||||
(type === 3 &&
|
||||
template.trim() &&
|
||||
!template.startsWith((adopted as Text).data))
|
||||
) {
|
||||
// TODO recover and provide more info
|
||||
throw new Error('hydration mismatch!')
|
||||
}
|
||||
}
|
||||
|
||||
currentHydrationNode = next(end!)
|
||||
return adopted
|
||||
}
|
|
@ -1,13 +1,33 @@
|
|||
import {
|
||||
adoptHydrationNode,
|
||||
currentHydrationNode,
|
||||
isHydrating,
|
||||
} from './hydration'
|
||||
import { child, createTextNode } from './node'
|
||||
|
||||
let t: HTMLTemplateElement
|
||||
|
||||
/*! #__NO_SIDE_EFFECTS__ */
|
||||
export function template(html: string, root?: boolean) {
|
||||
let node: ChildNode
|
||||
const create = () => {
|
||||
const t = document.createElement('template')
|
||||
t.innerHTML = html
|
||||
return t.content.firstChild!
|
||||
}
|
||||
let node: Node
|
||||
return (): Node & { $root?: true } => {
|
||||
const ret = (node || (node = create())).cloneNode(true)
|
||||
if (isHydrating) {
|
||||
if (__DEV__ && !currentHydrationNode) {
|
||||
// TODO this should not happen
|
||||
throw new Error('No current hydration node')
|
||||
}
|
||||
return adoptHydrationNode(currentHydrationNode, html)!
|
||||
}
|
||||
// fast path for text nodes
|
||||
if (html[0] !== '<') {
|
||||
return createTextNode(html)
|
||||
}
|
||||
if (!node) {
|
||||
t = t || document.createElement('template')
|
||||
t.innerHTML = html
|
||||
node = child(t.content)
|
||||
}
|
||||
const ret = node.cloneNode(true)
|
||||
if (root) (ret as any).$root = true
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// public APIs
|
||||
export { createVaporApp } from './apiCreateApp'
|
||||
export { createVaporApp, createVaporSSRApp } from './apiCreateApp'
|
||||
export { defineVaporComponent } from './apiDefineComponent'
|
||||
export { vaporInteropPlugin } from './vdomInterop'
|
||||
export type { VaporDirective } from './directives/custom'
|
||||
|
|
Loading…
Reference in New Issue