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
|
||||
}"
|
||||
`;
|
||||
|
||||
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
|
||||
}"
|
||||
`;
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
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', () => {
|
||||
const onError = vi.fn()
|
||||
const { code, ir, helpers } = compileWithVHtml(
|
||||
|
|
|
@ -58,6 +58,16 @@ describe('v-text', () => {
|
|||
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', () => {
|
||||
const onError = vi.fn()
|
||||
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 [
|
||||
NEWLINE,
|
||||
...inlineHandlers,
|
||||
`const n${operation.id} = `,
|
||||
...genCall(
|
||||
operation.dynamic && !operation.dynamic.isStatic
|
||||
isDynamicComponent
|
||||
? helper('createDynamicComponent')
|
||||
: operation.asset
|
||||
? helper('createComponentWithFallback')
|
||||
|
|
|
@ -7,10 +7,21 @@ export function genSetHtml(
|
|||
oper: SetHtmlIRNode,
|
||||
context: CodegenContext,
|
||||
): CodeFragment[] {
|
||||
const { helper } = context
|
||||
const {
|
||||
helper,
|
||||
block: { dynamicComponents },
|
||||
} = context
|
||||
|
||||
const isDynamicComponent = dynamicComponents.includes(oper.element)
|
||||
const { value, element } = oper
|
||||
return [
|
||||
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,
|
||||
context: CodegenContext,
|
||||
): CodeFragment[] {
|
||||
const { helper } = context
|
||||
const {
|
||||
helper,
|
||||
block: { dynamicComponents },
|
||||
} = context
|
||||
const { element, values, generated, jsx } = oper
|
||||
const texts = combineValues(values, context, jsx)
|
||||
return [
|
||||
NEWLINE,
|
||||
...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts),
|
||||
]
|
||||
|
||||
// if the element is a dynamic component, we need to use `setElementText`
|
||||
// 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(
|
||||
|
@ -40,6 +60,14 @@ export function genGetTextChild(
|
|||
oper: GetTextChildIRNode,
|
||||
context: CodegenContext,
|
||||
): 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 [
|
||||
NEWLINE,
|
||||
`const x${oper.parent} = ${context.helper('child')}(n${oper.parent})`,
|
||||
|
|
|
@ -49,6 +49,7 @@ export interface BlockIRNode extends BaseIRNode {
|
|||
type: IRNodeTypes.BLOCK
|
||||
node: RootNode | TemplateChildNode
|
||||
dynamic: IRDynamicInfo
|
||||
dynamicComponents: number[]
|
||||
tempId: number
|
||||
effect: IREffect[]
|
||||
operation: OperationNode[]
|
||||
|
|
|
@ -26,6 +26,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
|
|||
type: IRNodeTypes.BLOCK,
|
||||
node,
|
||||
dynamic: newDynamic(),
|
||||
dynamicComponents: [],
|
||||
effect: [],
|
||||
operation: [],
|
||||
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(
|
||||
el: Node & { $txt?: string },
|
||||
value: unknown,
|
||||
isConverted: boolean = false,
|
||||
): void {
|
||||
if (el.$txt !== (value = toDisplayString(value))) {
|
||||
if (el.$txt !== (value = isConverted ? value : toDisplayString(value))) {
|
||||
el.textContent = el.$txt = value as string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export {
|
|||
setProp,
|
||||
setDOMProp,
|
||||
setDynamicProps,
|
||||
setElementText,
|
||||
} from './dom/prop'
|
||||
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
|
||||
export { createIf } from './apiCreateIf'
|
||||
|
|
Loading…
Reference in New Issue