mirror of https://github.com/vuejs/core.git
feat: dynamic root nodes
This commit is contained in:
parent
f1e5bee7d5
commit
12187fbc85
|
@ -35,6 +35,8 @@ See the To-do list below or `// TODO` comments in code (`compiler-vapor` and `ru
|
|||
- [ ] Remove DOM API in codegen
|
||||
- [ ] Fragment
|
||||
- [x] multiple root nodes
|
||||
- [x] all dynamic children
|
||||
- [ ] return `Node[]` for all dynamic children, instead of using `fragment` API
|
||||
- [ ] Built-in Components
|
||||
- [ ] Transition
|
||||
- [ ] TransitionGroup
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`comile > bindings 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, createTextNode, insert, setText } from 'vue/vapor';
|
||||
const t0 = template(\`<div>count is <!>.</div>\`);
|
||||
const t0 = template('<div>count is <!>.</div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -27,7 +27,7 @@ export function render() {
|
|||
exports[`comile > directives > v-bind > simple expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setAttr } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -44,7 +44,7 @@ export function render() {
|
|||
exports[`comile > directives > v-html > no expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setHtml } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -61,7 +61,7 @@ export function render() {
|
|||
exports[`comile > directives > v-html > simple expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setHtml } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -78,7 +78,7 @@ export function render() {
|
|||
exports[`comile > directives > v-on > simple expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, on } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -95,7 +95,7 @@ export function render() {
|
|||
exports[`comile > directives > v-once > as root node 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setAttr } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -111,7 +111,7 @@ export function render() {
|
|||
|
||||
exports[`comile > directives > v-once > basic 1`] = `
|
||||
"import { template, children, createTextNode, insert, setText, setAttr } from 'vue/vapor';
|
||||
const t0 = template(\`<div> <span></span></div>\`);
|
||||
const t0 = template('<div> <span></span></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -134,7 +134,7 @@ export function render() {
|
|||
exports[`comile > directives > v-text > no expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setText } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -151,7 +151,7 @@ export function render() {
|
|||
exports[`comile > directives > v-text > simple expression 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, children, setText } from 'vue/vapor';
|
||||
const t0 = template(\`<div></div>\`);
|
||||
const t0 = template('<div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const {
|
||||
|
@ -165,9 +165,30 @@ export function render() {
|
|||
"
|
||||
`;
|
||||
|
||||
exports[`comile > dynamic root 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { fragment, createTextNode, insert, setText } from 'vue/vapor';
|
||||
export function render() {
|
||||
const t0 = fragment();
|
||||
const n0 = t0();
|
||||
const n1 = createTextNode(1);
|
||||
insert(n1, n0, 0 /* InsertPosition.FIRST */);
|
||||
const n2 = createTextNode(2);
|
||||
insert(n2, n0);
|
||||
watchEffect(() => {
|
||||
setText(n1, undefined, 1);
|
||||
});
|
||||
watchEffect(() => {
|
||||
setText(n2, undefined, 2);
|
||||
});
|
||||
return n0;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`comile > fragment 1`] = `
|
||||
"import { template } from 'vue/vapor';
|
||||
const t0 = template(\`<p></p><span></span><div></div>\`);
|
||||
const t0 = template('<p></p><span></span><div></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
return n0;
|
||||
|
@ -178,7 +199,7 @@ export function render() {
|
|||
exports[`comile > static + dynamic root 1`] = `
|
||||
"import { watchEffect } from 'vue';
|
||||
import { template, createTextNode, insert, setText } from 'vue/vapor';
|
||||
const t0 = template(\`2\`);
|
||||
const t0 = template('2');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
const n1 = createTextNode(1);
|
||||
|
@ -198,7 +219,7 @@ export function render() {
|
|||
|
||||
exports[`comile > static template 1`] = `
|
||||
"import { template } from 'vue/vapor';
|
||||
const t0 = template(\`<div><p>hello</p><input><span></span></div>\`);
|
||||
const t0 = template('<div><p>hello</p><input><span></span></div>');
|
||||
export function render() {
|
||||
const n0 = t0();
|
||||
return n0;
|
||||
|
|
|
@ -4,7 +4,7 @@ exports[`fixtures 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { watchEffect } from 'vue'
|
||||
import { template, children, createTextNode, insert, setText, on, setHtml } from 'vue/vapor'
|
||||
const t0 = template(\`<h1 id=\\"title\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\"text\\"><p>once: </p><p>{{ count }}</p>\`)
|
||||
const t0 = template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\")
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const html = '<b>HTML</b>'
|
||||
|
|
|
@ -28,6 +28,11 @@ describe('comile', () => {
|
|||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('dynamic root', async () => {
|
||||
const code = await compile(`{{ 1 }}{{ 2 }}`)
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('static + dynamic root', async () => {
|
||||
const code = await compile(`{{ 1 }}2{{ 3 }}`)
|
||||
expect(code).matchSnapshot()
|
||||
|
|
|
@ -18,14 +18,19 @@ export function generate(
|
|||
let preamble = ''
|
||||
|
||||
const { helpers, vaporHelpers } = ir
|
||||
if (ir.template.length) {
|
||||
preamble += ir.template
|
||||
.map(
|
||||
(template, i) => `const t${i} = template(\`${template.template}\`)\n`,
|
||||
)
|
||||
.join('')
|
||||
vaporHelpers.add('template')
|
||||
}
|
||||
|
||||
ir.template.forEach((template, i) => {
|
||||
if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
|
||||
preamble += `const t${i} = template(${JSON.stringify(
|
||||
template.template,
|
||||
)})\n`
|
||||
vaporHelpers.add('template')
|
||||
} else {
|
||||
// fragment
|
||||
code += `const t0 = fragment()\n`
|
||||
vaporHelpers.add('fragment')
|
||||
}
|
||||
})
|
||||
|
||||
{
|
||||
code += `const n${ir.children.id} = t0()\n`
|
||||
|
@ -50,6 +55,7 @@ export function generate(
|
|||
code += scope
|
||||
}
|
||||
// TODO multiple-template
|
||||
// TODO return statement in IR
|
||||
code += `return n${ir.children.id}\n`
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ import type { SourceLocation } from '@vue/compiler-dom'
|
|||
|
||||
export const enum IRNodeTypes {
|
||||
ROOT,
|
||||
TEMPLATE_GENERATOR,
|
||||
TEMPLATE_FACTORY,
|
||||
FRAGMENT_FACTORY,
|
||||
|
||||
SET_PROP,
|
||||
SET_TEXT,
|
||||
SET_EVENT,
|
||||
|
@ -19,7 +21,7 @@ export interface IRNode {
|
|||
|
||||
export interface RootIRNode extends IRNode {
|
||||
type: IRNodeTypes.ROOT
|
||||
template: Array<TemplateGeneratorIRNode>
|
||||
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
|
||||
children: DynamicChild
|
||||
// TODO multi-expression effect
|
||||
effect: Record<string /* expr */, OperationNode[]>
|
||||
|
@ -28,11 +30,15 @@ export interface RootIRNode extends IRNode {
|
|||
vaporHelpers: Set<string>
|
||||
}
|
||||
|
||||
export interface TemplateGeneratorIRNode extends IRNode {
|
||||
type: IRNodeTypes.TEMPLATE_GENERATOR
|
||||
export interface TemplateFactoryIRNode extends IRNode {
|
||||
type: IRNodeTypes.TEMPLATE_FACTORY
|
||||
template: string
|
||||
}
|
||||
|
||||
export interface FragmentFactoryIRNode extends IRNode {
|
||||
type: IRNodeTypes.FRAGMENT_FACTORY
|
||||
}
|
||||
|
||||
export interface SetPropIRNode extends IRNode {
|
||||
type: IRNodeTypes.SET_PROP
|
||||
element: number
|
||||
|
|
|
@ -73,11 +73,15 @@ function createRootContext(
|
|||
registerTemplate() {
|
||||
if (!ctx.template) return -1
|
||||
|
||||
const idx = ir.template.findIndex((t) => t.template === ctx.template)
|
||||
const idx = ir.template.findIndex(
|
||||
(t) =>
|
||||
t.type === IRNodeTypes.TEMPLATE_FACTORY &&
|
||||
t.template === ctx.template,
|
||||
)
|
||||
if (idx !== -1) return idx
|
||||
|
||||
ir.template.push({
|
||||
type: IRNodeTypes.TEMPLATE_GENERATOR,
|
||||
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||
template: ctx.template,
|
||||
loc: node.loc,
|
||||
})
|
||||
|
@ -153,6 +157,12 @@ export function transform(
|
|||
ghost: false,
|
||||
children: ctx.children,
|
||||
}
|
||||
if (ir.template.length === 0) {
|
||||
ir.template.push({
|
||||
type: IRNodeTypes.FRAGMENT_FACTORY,
|
||||
loc: root.loc,
|
||||
})
|
||||
}
|
||||
|
||||
return ir
|
||||
}
|
||||
|
|
|
@ -18,3 +18,7 @@ export const template = (str: string): (() => Node) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fragment(): () => DocumentFragment {
|
||||
return () => document.createDocumentFragment()
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<template>{{ '1' }}{{ '2' }}</template>
|
Loading…
Reference in New Issue