diff --git a/packages/compiler-dom/src/runtimeHelpers.ts b/packages/compiler-dom/src/runtimeHelpers.ts index c5349ad79..7a7ea01f2 100644 --- a/packages/compiler-dom/src/runtimeHelpers.ts +++ b/packages/compiler-dom/src/runtimeHelpers.ts @@ -20,6 +20,7 @@ export const V_ON_WITH_KEYS: unique symbol = Symbol( ) export const V_SHOW: unique symbol = Symbol(__DEV__ ? `vShow` : ``) +export const V_HTML: unique symbol = Symbol(__DEV__ ? `vHtml` : ``) export const TRANSITION: unique symbol = Symbol(__DEV__ ? `Transition` : ``) export const TRANSITION_GROUP: unique symbol = Symbol( @@ -35,6 +36,7 @@ registerRuntimeHelpers({ [V_ON_WITH_MODIFIERS]: `withModifiers`, [V_ON_WITH_KEYS]: `withKeys`, [V_SHOW]: `vShow`, + [V_HTML]: `vHtml`, [TRANSITION]: `Transition`, [TRANSITION_GROUP]: `TransitionGroup`, }) diff --git a/packages/compiler-dom/src/transforms/vHtml.ts b/packages/compiler-dom/src/transforms/vHtml.ts index 37e84bc61..2e3b824a7 100644 --- a/packages/compiler-dom/src/transforms/vHtml.ts +++ b/packages/compiler-dom/src/transforms/vHtml.ts @@ -1,13 +1,32 @@ import { + type ComponentNode, type DirectiveTransform, ElementTypes, + RESOLVE_DYNAMIC_COMPONENT, createObjectProperty, createSimpleExpression, + resolveComponentType, } from '@vue/compiler-core' import { DOMErrorCodes, createDOMCompilerError } from '../errors' +import { isObject } from '@vue/shared' +import { V_HTML } from '../runtimeHelpers' export const transformVHtml: DirectiveTransform = (dir, node, context) => { const { exp, loc } = dir + const { tag } = node + const isComponent = node.tagType === ElementTypes.COMPONENT + let vnodeTag = isComponent + ? resolveComponentType(node as ComponentNode, context) + : `"${tag}"` + + const isDynamicComponent = + isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT + if (isDynamicComponent) { + return { + props: [], + needRuntime: context.helper(V_HTML), + } + } if (node.tagType !== ElementTypes.ELEMENT) { context.onError( createDOMCompilerError(DOMErrorCodes.X_V_HTML_ON_INVALID_ELEMENT, loc), diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index f6a33f5a2..a36ed086e 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -12,7 +12,12 @@ return withDirectives(h(comp), [ */ import type { VNode } from './vnode' -import { EMPTY_OBJ, isBuiltInDirective, isFunction } from '@vue/shared' +import { + EMPTY_OBJ, + ShapeFlags, + isBuiltInDirective, + isFunction, +} from '@vue/shared' import { warn } from './warning' import { type ComponentInternalInstance, @@ -127,6 +132,12 @@ export type DirectiveArguments = Array< | [Directive | undefined, any, string | undefined, DirectiveModifiers] > +function isComponent(vnode: VNode): boolean { + const { shapeFlag } = vnode + if (shapeFlag & ShapeFlags.COMPONENT) return true + return false +} + /** * Adds directives to a VNode. */ @@ -143,6 +154,11 @@ export function withDirectives( for (let i = 0; i < directives.length; i++) { let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i] if (dir) { + // @ts-expect-error check v-html + if (isComponent(vnode) && dir.name === '__v-html') { + warn(`v-html can only be used on elements.`, vnode) + continue + } if (isFunction(dir)) { dir = { mounted: dir, diff --git a/packages/runtime-dom/src/directives/vHtml.ts b/packages/runtime-dom/src/directives/vHtml.ts new file mode 100644 index 000000000..c1e839b8b --- /dev/null +++ b/packages/runtime-dom/src/directives/vHtml.ts @@ -0,0 +1,23 @@ +import type { ObjectDirective } from '@vue/runtime-core' + +export type VHtmlElementDirective = ObjectDirective & { + name: '__v-html' +} +// only work with resolveDynamicComponent +export const vHtml: VHtmlElementDirective = { + name: '__v-html', + beforeMount(el, { value }) { + setInnerHTML(el, value) + }, + updated(el, { value, oldValue }) { + if (!value === !oldValue) return + setInnerHTML(el, value) + }, + beforeUnmount(el, { value }) { + setInnerHTML(el, value) + }, +} + +function setInnerHTML(el: HTMLElement, value: unknown): void { + el.innerHTML = String(value) +} diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index ca9a307dd..f626c00d0 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -282,6 +282,7 @@ export { } from './directives/vModel' export { withModifiers, withKeys } from './directives/vOn' export { vShow } from './directives/vShow' +export { vHtml } from './directives/vHtml' import { initVModelForSSR } from './directives/vModel' import { initVShowForSSR } from './directives/vShow'