feat(runtime-vapor): dynamic components work with v-html and v-text
This commit is contained in:
parent
88ef97ffd3
commit
a64867a500
|
@ -32,3 +32,13 @@ export function render(_ctx) {
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-html > work with dynamic component 1`] = `
|
||||||
|
"import { createDynamicComponent as _createDynamicComponent, setHtml as _setHtml, renderEffect as _renderEffect } from 'vue';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n0 = _createDynamicComponent(() => ('button'), null, null, true)
|
||||||
|
_renderEffect(() => _setHtml(n0.nodes, _ctx.foo))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -33,3 +33,13 @@ export function render(_ctx) {
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-text > work with dynamic component 1`] = `
|
||||||
|
"import { createDynamicComponent as _createDynamicComponent, toDisplayString as _toDisplayString, setElementText as _setElementText, renderEffect as _renderEffect } from 'vue';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n0 = _createDynamicComponent(() => ('button'), null, null, true)
|
||||||
|
_renderEffect(() => _setElementText(n0.nodes, _toDisplayString(_ctx.foo), true))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -54,6 +54,14 @@ describe('v-html', () => {
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('work with dynamic component', () => {
|
||||||
|
const { code } = compileWithVHtml(
|
||||||
|
`<component :is="'button'" v-html="foo"/>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains('setHtml(n0.nodes, _ctx.foo))')
|
||||||
|
})
|
||||||
|
|
||||||
test('should raise error and ignore children when v-html is present', () => {
|
test('should raise error and ignore children when v-html is present', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
const { code, ir, helpers } = compileWithVHtml(
|
const { code, ir, helpers } = compileWithVHtml(
|
||||||
|
|
|
@ -58,6 +58,16 @@ describe('v-text', () => {
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('work with dynamic component', () => {
|
||||||
|
const { code } = compileWithVText(
|
||||||
|
`<component :is="'button'" v-text="foo"/>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains(
|
||||||
|
'setElementText(n0.nodes, _toDisplayString(_ctx.foo), true)',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('should raise error and ignore children when v-text is present', () => {
|
test('should raise error and ignore children when v-text is present', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
const { code, ir } = compileWithVText(`<div v-text="test">hello</div>`, {
|
const { code, ir } = compileWithVText(`<div v-text="test">hello</div>`, {
|
||||||
|
|
|
@ -60,12 +60,15 @@ export function genCreateComponent(
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const isDynamicComponent = operation.dynamic && !operation.dynamic.isStatic
|
||||||
|
if (isDynamicComponent) context.block.dynamicComponents.push(operation.id)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
...inlineHandlers,
|
...inlineHandlers,
|
||||||
`const n${operation.id} = `,
|
`const n${operation.id} = `,
|
||||||
...genCall(
|
...genCall(
|
||||||
operation.dynamic && !operation.dynamic.isStatic
|
isDynamicComponent
|
||||||
? helper('createDynamicComponent')
|
? helper('createDynamicComponent')
|
||||||
: operation.asset
|
: operation.asset
|
||||||
? helper('createComponentWithFallback')
|
? helper('createComponentWithFallback')
|
||||||
|
|
|
@ -7,10 +7,21 @@ export function genSetHtml(
|
||||||
oper: SetHtmlIRNode,
|
oper: SetHtmlIRNode,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { helper } = context
|
const {
|
||||||
|
helper,
|
||||||
|
block: { dynamicComponents },
|
||||||
|
} = context
|
||||||
|
|
||||||
|
const isDynamicComponent = dynamicComponents.includes(oper.element)
|
||||||
const { value, element } = oper
|
const { value, element } = oper
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
...genCall(helper('setHtml'), `n${element}`, genExpression(value, context)),
|
...genCall(
|
||||||
|
helper('setHtml'),
|
||||||
|
// if the element is a dynamic component (VaporFragment)
|
||||||
|
// it should set html to the VaporFragment's nodes
|
||||||
|
`n${element}${isDynamicComponent ? '.nodes' : ''}`,
|
||||||
|
genExpression(value, context),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,33 @@ export function genSetText(
|
||||||
oper: SetTextIRNode,
|
oper: SetTextIRNode,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { helper } = context
|
const {
|
||||||
|
helper,
|
||||||
|
block: { dynamicComponents },
|
||||||
|
} = context
|
||||||
const { element, values, generated, jsx } = oper
|
const { element, values, generated, jsx } = oper
|
||||||
const texts = combineValues(values, context, jsx)
|
const texts = combineValues(values, context, jsx)
|
||||||
return [
|
|
||||||
NEWLINE,
|
// if the element is a dynamic component, we need to use `setElementText`
|
||||||
...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts),
|
// to set the textContent of the VaporFragment's nodes.
|
||||||
]
|
return dynamicComponents.includes(oper.element)
|
||||||
|
? [
|
||||||
|
NEWLINE,
|
||||||
|
...genCall(
|
||||||
|
helper('setElementText'),
|
||||||
|
`n${element}.nodes`,
|
||||||
|
texts,
|
||||||
|
'true', // isConverted
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
NEWLINE,
|
||||||
|
...genCall(
|
||||||
|
helper('setText'),
|
||||||
|
`${generated ? 'x' : 'n'}${element}`,
|
||||||
|
texts,
|
||||||
|
),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function combineValues(
|
function combineValues(
|
||||||
|
@ -40,6 +60,14 @@ export function genGetTextChild(
|
||||||
oper: GetTextChildIRNode,
|
oper: GetTextChildIRNode,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
|
const {
|
||||||
|
block: { dynamicComponents },
|
||||||
|
} = context
|
||||||
|
|
||||||
|
// if the parent is a dynamic component, don't need to generate a child
|
||||||
|
// because it will use the `setElementText` helper directly.
|
||||||
|
if (dynamicComponents.includes(oper.parent)) return []
|
||||||
|
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
`const x${oper.parent} = ${context.helper('child')}(n${oper.parent})`,
|
`const x${oper.parent} = ${context.helper('child')}(n${oper.parent})`,
|
||||||
|
|
|
@ -49,6 +49,7 @@ export interface BlockIRNode extends BaseIRNode {
|
||||||
type: IRNodeTypes.BLOCK
|
type: IRNodeTypes.BLOCK
|
||||||
node: RootNode | TemplateChildNode
|
node: RootNode | TemplateChildNode
|
||||||
dynamic: IRDynamicInfo
|
dynamic: IRDynamicInfo
|
||||||
|
dynamicComponents: number[]
|
||||||
tempId: number
|
tempId: number
|
||||||
effect: IREffect[]
|
effect: IREffect[]
|
||||||
operation: OperationNode[]
|
operation: OperationNode[]
|
||||||
|
|
|
@ -26,6 +26,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
node,
|
node,
|
||||||
dynamic: newDynamic(),
|
dynamic: newDynamic(),
|
||||||
|
dynamicComponents: [],
|
||||||
effect: [],
|
effect: [],
|
||||||
operation: [],
|
operation: [],
|
||||||
returns: [],
|
returns: [],
|
||||||
|
|
|
@ -185,13 +185,16 @@ export function setText(el: Text & { $txt?: string }, value: string): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by setDynamicProps only, so need to guard with `toDisplayString`
|
* Used by
|
||||||
|
* - setDynamicProps, need to guard with `toDisplayString`
|
||||||
|
* - v-text on dynamic component, value passed here is already converted
|
||||||
*/
|
*/
|
||||||
export function setElementText(
|
export function setElementText(
|
||||||
el: Node & { $txt?: string },
|
el: Node & { $txt?: string },
|
||||||
value: unknown,
|
value: unknown,
|
||||||
|
isConverted: boolean = false,
|
||||||
): void {
|
): void {
|
||||||
if (el.$txt !== (value = toDisplayString(value))) {
|
if (el.$txt !== (value = isConverted ? value : toDisplayString(value))) {
|
||||||
el.textContent = el.$txt = value as string
|
el.textContent = el.$txt = value as string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ export {
|
||||||
setProp,
|
setProp,
|
||||||
setDOMProp,
|
setDOMProp,
|
||||||
setDynamicProps,
|
setDynamicProps,
|
||||||
|
setElementText,
|
||||||
} from './dom/prop'
|
} from './dom/prop'
|
||||||
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
|
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
|
||||||
export { createIf } from './apiCreateIf'
|
export { createIf } from './apiCreateIf'
|
||||||
|
|
Loading…
Reference in New Issue