当前位置: 首页 > news >正文

【源码分析】@vue/runtime-dom/src/apiCustomElement.ts 解析

源码来源于@vue/runtime-dom/src/apiCustomElement.ts
其他参考 @vue/runtime-core/src/apiDefineComponent.ts@vue/runtime-core/src/componentProps.ts@vue/shared/src/general.ts


export type VueElementConstructor<P = {}> = {new (initialProps?: Record<string, any>): VueElement & P
}

这段代码定义了一个可导出的泛型类型VueElementConstructor,用于描述能创建VueElement实例的构造函数类型,该构造函数可接收可选的任意属性对象作为初始参数,返回的实例同时具备VueElement类型和泛型参数P(默认空对象)所指定的类型特性,主要作用是在TypeScript中约束和描述Vue元素构造函数的参数和返回值类型,确保类型安全。


export interface CustomElementOptions {styles?: string[]shadowRoot?: booleannonce?: stringconfigureApp?: (app: App) => void
}

这段代码定义了一个可导出的接口CustomElementOptions,用于规范自定义元素(通常是Vue自定义元素)的配置选项结构,包含可选的样式数组(styles)、是否启用影子DOM(shadowRoot)、安全随机数(nonce),以及用于配置Vue应用实例的函数(configureApp),核心作用是为自定义元素的创建提供统一的类型约束,确保配置项的类型安全和使用规范性。

defineCustomElement 重载


// overload 1: direct setup function
export function defineCustomElement<Props, RawBindings = object>(setup: (props: Props, ctx: SetupContext) => RawBindings | RenderFunction,options?: Pick<ComponentOptions, 'name' | 'inheritAttrs' | 'emits'> &CustomElementOptions & {props?: (keyof Props)[]},
): VueElementConstructor<Props>

这段代码定义了defineCustomElement函数的第一个重载形式,用于接收一个setup函数和包含组件名称、属性继承、事件声明等的配置选项,返回能创建对应Vue元素实例的构造函数,主要作用是提供一种通过setup函数定义Vue自定义元素的方式,并通过TypeScript泛型确保属性等类型的一致性。

export function defineCustomElement<Props, RawBindings = object>(setup: (props: Props, ctx: SetupContext) => RawBindings | RenderFunction,options?: Pick<ComponentOptions, 'name' | 'inheritAttrs' | 'emits'> &CustomElementOptions & {props?: ComponentObjectPropsOptions<Props>},
): VueElementConstructor<Props>

这段代码定义了defineCustomElement函数的一种重载形式,用于接收setup函数作为核心逻辑入口来创建Vue自定义元素,setup函数接收props和上下文对象并返回绑定数据或渲染函数;第二个参数为可选配置,可包含组件名称、属性继承、事件声明等组件选项及自定义元素配置,且支持以对象形式定义props;最终返回对应Props类型的Vue自定义元素构造函数,作用是提供一种基于setup函数和对象式属性配置定义类型安全的自定义元素的方式。


// overload 2: defineCustomElement with options object, infer props from options
export function defineCustomElement<// propsRuntimePropsOptions extendsComponentObjectPropsOptions = ComponentObjectPropsOptions,PropsKeys extends string = string,// emitsRuntimeEmitsOptions extends EmitsOptions = {},EmitsKeys extends string = string,// other optionsData = {},SetupBindings = {},Computed extends ComputedOptions = {},Methods extends MethodOptions = {},Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,Extends extends ComponentOptionsMixin = ComponentOptionsMixin,InjectOptions extends ComponentInjectOptions = {},InjectKeys extends string = string,Slots extends SlotsType = {},LocalComponents extends Record<string, Component> = {},Directives extends Record<string, Directive> = {},Exposed extends string = string,Provide extends ComponentProvideOptions = ComponentProvideOptions,// resolved typesInferredProps = string extends PropsKeys? ComponentObjectPropsOptions extends RuntimePropsOptions? {}: ExtractPropTypes<RuntimePropsOptions>: { [key in PropsKeys]?: any },ResolvedProps = InferredProps & EmitsToProps<RuntimeEmitsOptions>,
>(options: CustomElementOptions & {props?: (RuntimePropsOptions & ThisType<void>) | PropsKeys[]} & ComponentOptionsBase<ResolvedProps,SetupBindings,Data,Computed,Methods,Mixin,Extends,RuntimeEmitsOptions,EmitsKeys,{}, // DefaultsInjectOptions,InjectKeys,Slots,LocalComponents,Directives,Exposed,Provide> &ThisType<CreateComponentPublicInstanceWithMixins<Readonly<ResolvedProps>,SetupBindings,Data,Computed,Methods,Mixin,Extends,RuntimeEmitsOptions,EmitsKeys,{},false,InjectOptions,Slots,LocalComponents,Directives,Exposed>>,extraOptions?: CustomElementOptions,
): VueElementConstructor<ResolvedProps>

这段代码定义了defineCustomElement函数的第二种重载形式,支持通过包含自定义元素配置与组件完整选项(如props、emits、data等)的选项对象来定义Vue自定义元素,通过复杂泛型逻辑从选项中自动推断属性(props)类型,最终返回对应解析后属性类型的Vue自定义元素构造函数,作用是提供一种基于完整选项对象定义自定义元素的方式,同时确保类型推断的准确性和对组件各类选项的全面支持。


// overload 3: defining a custom element from the returned value of
// `defineComponent`
export function defineCustomElement<// this should be `ComponentPublicInstanceConstructor` but that type is not exportedT extends { new (...args: any[]): ComponentPublicInstance<any> },
>(options: T,extraOptions?: CustomElementOptions,
): VueElementConstructor<T extends DefineComponent<infer P, any, any, any> ? P : unknown
>

这段代码是defineCustomElement函数的第三种重载形式,用于接收defineComponent返回的组件构造函数,结合可选的自定义元素配置,通过泛型推断组件属性类型,最终返回对应属性类型的Vue自定义元素构造函数,作用是支持将已用defineComponent定义的组件直接转换为自定义元素,实现组件复用。


/*! #__NO_SIDE_EFFECTS__ */
export function defineCustomElement(options: any,extraOptions?: ComponentOptions,/*** @internal*/_createApp?: CreateAppFunction<Element>,
): VueElementConstructor {const Comp = defineComponent(options, extraOptions) as anyif (isPlainObject(Comp)) extend(Comp, extraOptions)class VueCustomElement extends VueElement {static def = Compconstructor(initialProps?: Record<string, any>) {super(Comp, initialProps, _createApp)}}return VueCustomElement
}

这段代码是defineCustomElement函数的核心实现,通过调用defineComponent处理传入的组件选项及额外配置,合并为标准组件对象,再创建继承自VueElement的自定义元素类,该类存储组件定义并在构造时初始化,最终返回可用于注册为Web Components的Vue自定义元素构造函数,实现将Vue组件转换为原生自定义元素。

VueElement



export class VueElementextends BaseClassimplements ComponentCustomElementInterface
{_isVueCE = true/*** @internal*/_instance: ComponentInternalInstance | null = null/*** @internal*/_app: App | null = null/*** @internal*/_root: Element | ShadowRoot/*** @internal*/_nonce: string | undefined = this._def.nonce/*** @internal*/_teleportTarget?: HTMLElementprivate _connected = falseprivate _resolved = falseprivate _numberProps: Record<string, true> | null = nullprivate _styleChildren = new WeakSet()private _pendingResolve: Promise<void> | undefinedprivate _parent: VueElement | undefined/*** dev only*/private _styles?: HTMLStyleElement[]/*** dev only*/private _childStyles?: Map<string, HTMLStyleElement[]>private _ob?: MutationObserver | null = nullprivate _slots?: Record<string, Node[]>constructor(/*** Component def - note this may be an AsyncWrapper, and this._def will* be overwritten by the inner component when resolved.*/private _def: InnerComponentDef,private _props: Record<string, any> = {},private _createApp: CreateAppFunction<Element> = createApp,) {super()if (this.shadowRoot && _createApp !== createApp) {this._root = this.shadowRoot} else {if (__DEV__ && this.shadowRoot) {warn(`Custom element has pre-rendered declarative shadow root but is not ` +`defined as hydratable. Use \`defineSSRCustomElement\`.`,)}if (_def.shadowRoot !== false) {this.attachShadow({ mode: 'open' })this._root = this.shadowRoot!} else {this._root = this}}}connectedCallback(): void {// avoid resolving component if it's not connectedif (!this.isConnected) return// avoid re-parsing slots if already resolvedif (!this.shadowRoot && !this._resolved) {this._parseSlots()}this._connected = true// locate nearest Vue custom element parent for provide/injectlet parent: Node | null = thiswhile ((parent = parent && (parent.parentNode || (parent as ShadowRoot).host))) {if (parent instanceof VueElement) {this._parent = parentbreak}}if (!this._instance) {if (this._resolved) {this._mount(this._def)} else {if (parent && parent._pendingResolve) {this._pendingResolve = parent._pendingResolve.then(() => {this._pendingResolve = undefinedthis._resolveDef()})} else {this._resolveDef()}}}}private _setParent(parent = this._parent) {if (parent) {this._instance!.parent = parent._instancethis._inheritParentContext(parent)}}private _inheritParentContext(parent = this._parent) {// #13212, the provides object of the app context must inherit the provides// object from the parent element so we can inject values from both placesif (parent && this._app) {Object.setPrototypeOf(this._app._context.provides,parent._instance!.provides,)}}disconnectedCallback(): void {this._connected = falsenextTick(() => {if (!this._connected) {if (this._ob) {this._ob.disconnect()this._ob = null}// unmountthis._app && this._app.unmount()if (this._instance) this._instance.ce = undefinedthis._app = this._instance = null}})}/*** resolve inner component definition (handle possible async component)*/private _resolveDef() {if (this._pendingResolve) {return}// set initial attrsfor (let i = 0; i < this.attributes.length; i++) {this._setAttr(this.attributes[i].name)}// watch future attr changesthis._ob = new MutationObserver(mutations => {for (const m of mutations) {this._setAttr(m.attributeName!)}})this._ob.observe(this, { attributes: true })const resolve = (def: InnerComponentDef, isAsync = false) => {this._resolved = truethis._pendingResolve = undefinedconst { props, styles } = def// cast Number-type props set before resolvelet numberPropsif (props && !isArray(props)) {for (const key in props) {const opt = props[key]if (opt === Number || (opt && opt.type === Number)) {if (key in this._props) {this._props[key] = toNumber(this._props[key])};(numberProps || (numberProps = Object.create(null)))[camelize(key)] = true}}}this._numberProps = numberPropsthis._resolveProps(def)// apply CSSif (this.shadowRoot) {this._applyStyles(styles)} else if (__DEV__ && styles) {warn('Custom element style injection is not supported when using ' +'shadowRoot: false',)}// initial mountthis._mount(def)}const asyncDef = (this._def as ComponentOptions).__asyncLoaderif (asyncDef) {this._pendingResolve = asyncDef().then((def: InnerComponentDef) => {def.configureApp = this._def.configureAppresolve((this._def = def), true)})} else {resolve(this._def)}}private _mount(def: InnerComponentDef) {if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && !def.name) {// @ts-expect-errordef.name = 'VueElement'}this._app = this._createApp(def)// inherit before configureApp to detect context overwritesthis._inheritParentContext()if (def.configureApp) {def.configureApp(this._app)}this._app._ceVNode = this._createVNode()this._app.mount(this._root)// apply expose after mountconst exposed = this._instance && this._instance.exposedif (!exposed) returnfor (const key in exposed) {if (!hasOwn(this, key)) {// exposed properties are readonlyObject.defineProperty(this, key, {// unwrap ref to be consistent with public instance behaviorget: () => unref(exposed[key]),})} else if (__DEV__) {warn(`Exposed property "${key}" already exists on custom element.`)}}}private _resolveProps(def: InnerComponentDef) {const { props } = defconst declaredPropKeys = isArray(props) ? props : Object.keys(props || {})// check if there are props set pre-upgrade or connectfor (const key of Object.keys(this)) {if (key[0] !== '_' && declaredPropKeys.includes(key)) {this._setProp(key, this[key as keyof this])}}// defining getter/setters on prototypefor (const key of declaredPropKeys.map(camelize)) {Object.defineProperty(this, key, {get() {return this._getProp(key)},set(val) {this._setProp(key, val, true, true)},})}}protected _setAttr(key: string): void {if (key.startsWith('data-v-')) returnconst has = this.hasAttribute(key)let value = has ? this.getAttribute(key) : REMOVALconst camelKey = camelize(key)if (has && this._numberProps && this._numberProps[camelKey]) {value = toNumber(value)}this._setProp(camelKey, value, false, true)}/*** @internal*/protected _getProp(key: string): any {return this._props[key]}/*** @internal*/_setProp(key: string,val: any,shouldReflect = true,shouldUpdate = false,): void {if (val !== this._props[key]) {if (val === REMOVAL) {delete this._props[key]} else {this._props[key] = val// support set key on ceVNodeif (key === 'key' && this._app) {this._app._ceVNode!.key = val}}if (shouldUpdate && this._instance) {this._update()}// reflectif (shouldReflect) {const ob = this._obob && ob.disconnect()if (val === true) {this.setAttribute(hyphenate(key), '')} else if (typeof val === 'string' || typeof val === 'number') {this.setAttribute(hyphenate(key), val + '')} else if (!val) {this.removeAttribute(hyphenate(key))}ob && ob.observe(this, { attributes: true })}}}private _update() {const vnode = this._createVNode()if (this._app) vnode.appContext = this._app._contextrender(vnode, this._root)}private _createVNode(): VNode<any, any> {const baseProps: VNodeProps = {}if (!this.shadowRoot) {baseProps.onVnodeMounted = baseProps.onVnodeUpdated =this._renderSlots.bind(this)}const vnode = createVNode(this._def, extend(baseProps, this._props))if (!this._instance) {vnode.ce = instance => {this._instance = instanceinstance.ce = thisinstance.isCE = true // for vue-i18n backwards compat// HMRif (__DEV__) {instance.ceReload = newStyles => {// always reset stylesif (this._styles) {this._styles.forEach(s => this._root.removeChild(s))this._styles.length = 0}this._applyStyles(newStyles)this._instance = nullthis._update()}}const dispatch = (event: string, args: any[]) => {this.dispatchEvent(new CustomEvent(event,isPlainObject(args[0])? extend({ detail: args }, args[0]): { detail: args },),)}// intercept emitinstance.emit = (event: string, ...args: any[]) => {// dispatch both the raw and hyphenated versions of an event// to match Vue behaviordispatch(event, args)if (hyphenate(event) !== event) {dispatch(hyphenate(event), args)}}this._setParent()}}return vnode}private _applyStyles(styles: string[] | undefined,owner?: ConcreteComponent,) {if (!styles) returnif (owner) {if (owner === this._def || this._styleChildren.has(owner)) {return}this._styleChildren.add(owner)}const nonce = this._noncefor (let i = styles.length - 1; i >= 0; i--) {const s = document.createElement('style')if (nonce) s.setAttribute('nonce', nonce)s.textContent = styles[i]this.shadowRoot!.prepend(s)// record for HMRif (__DEV__) {if (owner) {if (owner.__hmrId) {if (!this._childStyles) this._childStyles = new Map()let entry = this._childStyles.get(owner.__hmrId)if (!entry) {this._childStyles.set(owner.__hmrId, (entry = []))}entry.push(s)}} else {;(this._styles || (this._styles = [])).push(s)}}}}/*** Only called when shadowRoot is false*/private _parseSlots() {const slots: VueElement['_slots'] = (this._slots = {})let nwhile ((n = this.firstChild)) {const slotName =(n.nodeType === 1 && (n as Element).getAttribute('slot')) || 'default';(slots[slotName] || (slots[slotName] = [])).push(n)this.removeChild(n)}}/*** Only called when shadowRoot is false*/private _renderSlots() {const outlets = (this._teleportTarget || this).querySelectorAll('slot')const scopeId = this._instance!.type.__scopeIdfor (let i = 0; i < outlets.length; i++) {const o = outlets[i] as HTMLSlotElementconst slotName = o.getAttribute('name') || 'default'const content = this._slots![slotName]const parent = o.parentNode!if (content) {for (const n of content) {// for :slotted cssif (scopeId && n.nodeType === 1) {const id = scopeId + '-s'const walker = document.createTreeWalker(n, 1);(n as Element).setAttribute(id, '')let childwhile ((child = walker.nextNode())) {;(child as Element).setAttribute(id, '')}}parent.insertBefore(n, o)}} else {while (o.firstChild) parent.insertBefore(o.firstChild, o)}parent.removeChild(o)}}/*** @internal*/_injectChildStyle(comp: ConcreteComponent & CustomElementOptions): void {this._applyStyles(comp.styles, comp)}/*** @internal*/_removeChildStyle(comp: ConcreteComponent): void {if (__DEV__) {this._styleChildren.delete(comp)if (this._childStyles && comp.__hmrId) {// clear old stylesconst oldStyles = this._childStyles.get(comp.__hmrId)if (oldStyles) {oldStyles.forEach(s => this._root.removeChild(s))oldStyles.length = 0}}}}
}

整体内容与作用

VueElement 是 Vue 自定义元素(Web Components)的核心基类,继承自 BaseClass 并实现 ComponentCustomElementInterface负责将 Vue 组件逻辑与原生 Web Components 规范深度整合:管理组件内部实例(_instance)、渲染根节点(_root,ShadowRoot 或元素自身)、生命周期(连接/断开 DOM)、props 传递与同步、样式注入、插槽解析渲染,以及将 Vue 组件的 emit 事件转为原生 CustomEvent,最终实现 Vue 组件向原生自定义元素的适配,支持跨框架复用。

各方法/构造函数的内容与作用

1. 构造函数 constructor(_def, _props?, _createApp?)
  • 内容:初始化自定义元素基础状态,处理 Shadow DOM 配置:
    • 接收组件定义(_def)、初始 props(_props)和 App 创建函数(_createApp);
    • 若元素已有预渲染的声明式 ShadowRoot 且使用自定义 _createApp,直接将其设为 _root
    • _def.shadowRoot 不为 false,创建开放模式(open)的 ShadowRoot 作为 _root,否则以元素自身为 _root
    • 开发环境下,对已有 ShadowRoot 但非 hydration 场景给出警告。
  • 作用:奠定自定义元素的渲染基础,确定渲染根节点,初始化核心配置。
2. 生命周期回调 connectedCallback()
  • 内容:原生 Web Components 生命周期,当元素插入 DOM 时触发:
    • 检查元素是否真的连接(isConnected),避免无效执行;
    • 未使用 ShadowRoot 且未解析组件时,解析插槽(_parseSlots);
    • 查找最近的父级 VueElement(用于 provide/inject 上下文继承);
    • 若未创建组件实例(_instance),已解析组件则直接挂载(_mount),未解析则触发组件定义解析(_resolveDef),父组件未解析时等待父解析完成。
  • 作用:触发自定义元素的初始化流程,关联父组件上下文,启动组件解析与挂载。
3. 私有方法 _setParent(parent?)
  • 内容:将当前自定义元素的组件实例(_instance)的 parent 指向父 VueElement 的组件实例。
  • 作用:建立自定义元素间的组件实例层级,确保组件树关系正确(如父子组件通信、生命周期传递)。
4. 私有方法 _inheritParentContext(parent?)
  • 内容:若存在父 VueElement 且当前已创建 App(_app),将 App 上下文的 provides 原型设为父组件实例的 provides
  • 作用:继承父组件的 provide 上下文,确保 inject API 在自定义元素层级间生效(如跨自定义元素注入值)。
5. 生命周期回调 disconnectedCallback()
  • 内容:原生 Web Components 生命周期,当元素从 DOM 移除时触发:
    • 标记 _connectedfalse,延迟(nextTick)执行清理逻辑;
    • 断开 MutationObserver(停止监听属性变化);
    • 卸载 App(_app.unmount()),销毁组件实例(_instance),重置 _app_instance
  • 作用:避免内存泄漏,清理自定义元素关联的资源(监听、组件实例、App)。
6. 私有方法 _resolveDef()
  • 内容:解析组件定义(处理同步/异步组件),初始化 props 和样式:
    • 监听自定义元素的属性变化(MutationObserver),属性变化时触发 _setAttr
    • 处理异步组件:若组件定义含 __asyncLoader,加载完成后更新 _def 并继续解析;
    • 同步组件:转换 Number 类型 props 的初始值,解析 props 并定义 getter/setter(_resolveProps),注入组件样式(_applyStyles)。
  • 作用:完成组件定义的解析(尤其是异步组件),同步初始属性,为组件挂载做准备。
7. 私有方法 _mount(def)
  • 内容:创建 Vue App 并挂载组件到渲染根(_root):
    • 开发/生产调试模式下,为组件定义补充默认名称(VueElement);
    • 调用 _createApp 创建 App,继承父上下文(_inheritParentContext),执行组件的 configureApp 配置;
    • 创建组件 VNode(_createVNode)并挂载到 _root
    • 组件暴露(exposed)的属性,在自定义元素上定义只读 getter(解包 ref,与 Vue 实例行为一致)。
  • 作用:启动 Vue 组件的渲染流程,将组件挂载到自定义元素的渲染根,暴露组件属性供外部访问。
8. 私有方法 _resolveProps(def)
  • 内容:解析组件声明的 props,定义自定义元素的 props 访问器:
    • 提取组件声明的 props 键(数组/对象形式);
    • 同步自定义元素自身已有的 props 值到 _props
    • 为每个 props 键(转为驼峰)定义 getter(调用 _getProp)和 setter(调用 _setProp)。
  • 作用:实现自定义元素的 props 与 Vue 组件 props 的双向绑定,确保外部修改自定义元素属性时同步到组件内部。
9. 保护方法 _setAttr(key)
  • 内容:处理自定义元素的属性变化(排除 data-v- 类属性):
    • 将属性名转为驼峰(camelize),判断是否为 Number 类型 props,若则转换属性值为 Number;
    • 调用 _setProp 更新对应的 props,不反射属性到 DOM(shouldReflect=false)。
  • 作用:将原生属性变化同步到组件 props,处理属性类型转换(如字符串转 Number)。
10. 保护方法 _getProp(key)
  • 内容:返回 _props 中指定键的值。
  • 作用:作为 props getter 的底层实现,提供组件 props 的读取能力。
11. 内部方法 _setProp(key, val, shouldReflect?, shouldUpdate?)
  • 内容:更新 props 并同步到组件/DOM:
    • 若值变化,更新 _props(值为 REMOVAL 时删除键),keykey 时同步 VNode 的 key
    • shouldUpdate=true 时,触发组件重新渲染(_update);
    • shouldReflect=true 时,将 props 变化反射到自定义元素的 DOM 属性(驼峰转连字符,布尔值特殊处理)。
  • 作用:实现 props 的更新逻辑,同步组件内部状态与外部 DOM 属性,触发组件重渲染。
12. 私有方法 _update()
  • 内容:创建新的组件 VNode,继承 App 上下文,重新渲染到 _root(调用 render)。
  • 作用:触发组件的重新渲染,响应 props 变化或其他需要更新视图的场景。
13. 私有方法 _createVNode()
  • 内容:创建组件对应的 VNode,配置组件实例回调:
    • 非 ShadowRoot 场景,为 VNode 绑定 onVnodeMounted/onVnodeUpdated(触发 _renderSlots);
    • 首次创建时,在 VNode 的 ce 回调中初始化组件实例(_instance),拦截 emit 转为原生 CustomEvent(同时触发原始名和连字符名事件);
    • 处理开发环境 HMR:定义 ceReload 方法,支持样式重载和组件重新渲染。
  • 作用:创建 Vue 组件的 VNode 载体,关联自定义元素与组件实例,适配 emit 事件为原生规范。
14. 私有方法 _applyStyles(styles, owner?)
  • 内容:将组件样式注入到 ShadowRoot(非 ShadowRoot 场景开发环境警告):
    • 为每个样式字符串创建 <style> 标签,添加 nonce(若有),插入到 ShadowRoot 顶部;
    • 开发环境下,记录样式(自身样式存 _styles,子组件样式存 _childStyles),用于 HMR 清理。
  • 作用:实现组件样式的隔离注入(依赖 ShadowRoot),支持样式 HMR。
15. 私有方法 _parseSlots()
  • 内容:非 ShadowRoot 场景下,解析自定义元素的子节点为插槽:
    • 遍历子节点,根据 slot 属性分类存储到 _slots(默认插槽存 default 键),并从自定义元素中移除子节点(避免原生渲染)。
  • 作用:为非 ShadowRoot 场景准备插槽内容,适配 Vue 的插槽逻辑。
16. 私有方法 _renderSlots()
  • 内容:非 ShadowRoot 场景下,将解析的插槽内容渲染到 <slot> 出口:
    • 查找所有 <slot> 元素,根据 name 属性匹配 _slots 中的内容;
    • 插入插槽内容到 <slot> 位置,移除原生 <slot> 元素;
    • 处理 scoped 样式:为插槽元素添加 scopeId-s 属性,确保 :slotted 选择器生效。
  • 作用:在非 ShadowRoot 场景下实现 Vue 插槽的渲染逻辑,适配 scoped 样式。
17. 内部方法 _injectChildStyle(comp)
  • 内容:调用 _applyStyles 注入子组件(comp)的样式到当前自定义元素的 ShadowRoot。
  • 作用:支持子组件样式在父自定义元素的 ShadowRoot 中隔离注入。
18. 内部方法 _removeChildStyle(comp)
  • 内容:开发环境下,删除子组件(comp)在当前自定义元素中的样式:
    • _styleChildren 中移除子组件,清理 _childStyles 中对应的样式标签并从 DOM 移除。
  • 作用:配合 HMR 或子组件卸载,清理子组件样式,避免样式污染。

参考资料

  • 【源码阅读】@vue/shared —— Vue3 内部工具函数库
  • 自定义元素最佳做法
http://www.dtcms.com/a/353916.html

相关文章:

  • 重磅升级,Pixso 2.0赋能HarmonyOS应用设计与开发
  • 安卓11 12系统修改定制化_____如何修改固件 实现给指定内置的应用无障碍权限
  • Sybase 安装与备份
  • 【c++】超好玩游戏
  • 一、CSS3 新增选择器(非 “属性”,但为核心基础)
  • day082-初识ElasticStack
  • 路由基础(二):路由表和FIB表
  • Ansible文件管理与Jinja2模板
  • Linux查看SFTP登录不上的问题以及解决
  • 【Simulink专题】Simulink模型:MIL单元测试
  • 宝塔发布ktg-mes
  • vue cli 没使用的图片会被打包吗
  • 2025年08月27日Github流行趋势
  • 怎么更新 cargo.exe ?(Rust 工具链)
  • etcd-基本工作原理及部署
  • react + G2(v4) 应用
  • 【C++游记】模板升级
  • 飞腾 D2000 八核处理器板卡深度解析:全国产化硬件方案与丰富扩展能力
  • Linux中创建SFTP账号
  • Netty:现代网络应用的利器
  • 软件定义汽车(SDV)调试——如何做到 适配软件定义汽车(SDV)?(中)
  • 造作AI-你的人工智能创作助手
  • 某中医院信息化能力提升:智能组网设备助力网络架构优化
  • 【日常学习】2025-8-27 测开框架设计模式探索04
  • Element整体操作样式
  • 数据分析编程第五步:数据准备与整理
  • DDD之事件机制(9)
  • 沃丰科技出海客服系统对接沃尔玛全球电商平台,赋能中企出海
  • 升级DrRacket8.10到8.18版本@Ubuntu24.04
  • GitLab 导入/导出仓库