-
Notifications
You must be signed in to change notification settings - Fork 101
Description
TL;DR
Issue:
vue-web-component-wrapper prevents a WebComponent #shadow-root from containing DOM rendered slot elements.
In its implementation, the wrap function it provides is focused on creating CustomElements tags that behave as VueComponents; rather than focusing on behaving as WebComponents defined from a VueComponent that integrates, with shadow-DOM semantics, as a direct child-element in their #shadow-root definition.
I've written a Vue
vue-web-component-plugin(<=) , using a different design, that supports DOMWebComponentslotbehavior. It is approximately ~100 lines of self-contained ready to use Javascript.msjmodule code.Some of the other differences from
wrapare:
- seamless
Vue.componentregistration viaVue.component(..)- any ancestral
$rootvueis inherited/shared, or if none, then a$rootvueis created as needed- lazy Vue integration of DOM upgraded custom
HTMLElements; reducing temporal side-effects.vueDefinitionkeyshadowCsssupport for#shadow-rootencapsulatedcssdefinitionsFor
slotsto work, I had to enhance Vue with a way to honor DOM semantics for#shadow-rootslotelements.
Note:
The following discussion assumes some familiarity with the DOM concepts slot, light DOM and shadow DOM.
For more detail on what
wrapdoes, and why it is different from thepluginI mentioned above, I will briefly present a minimal description of how VueVueComponentswork and describe a few browser terms for clarity.
The Vue system has VueComponents (similar to classes) that are based on vueDefinitions associated with a tagName using Vue.component(tagName, vueDefinition). I will be referring to these VueComponents as HTML macros, from here on.
When you create a new $root vue using new Vue(vueDefinition) with a el: member in the vueDefinition (or manually invoke its' $mount), Vue will walk the childElements and recursively perform macro substitution of all the elements whose tagName matches the VueComponents you registered using Vue.component(tagName, vueDefinition). It will continue to perform this behavior based on reactive change tracking from that point on.
It does this using the render function defined on the vueDefinition.
If no render function is defined, it will look for a template to compile into a render function. If no template is defined, it will extract the innerHTML of the corresponding tagName element, and use that as the template.
This makes Vue a nicely designed and powerful reactive HTML macro (aka
VueComponent) system, where arguments to thetagNameelement macros are provided in their attributes, and allrenderedmacro results are reactively tracked for efficient management of change propagation (observe and react patterns from Smalltalk). In that same systemtemplatesand (innerHTML) support{{..}}substitution rules.
The browser DOM allows you to define CustomElements and WebComponents.
To enable that, it provides the customElements API to allow registering a Javascript class as the behavior for an element with a hyphenated tagName.
A custom element once registered is a new type of browser DOM element; depending on your browser, custom elements technology is the same technique by which it internally implements a number of the intrinsic browser elements with closed #shadow-roots.
A custom element is not a macro and thus can't do some of the things Vue VueComponents can, but a WebComponent using a shadow DOM #shadow-root can provide some capabilities that Vue VueComponents cannot. This can be specifically seen in how it uses DOM slot elements, and how it lazily upgrades tagName elements when a corresponding custom element is registered, and in how it encapsulates the #shadow-root elements including css.
When you issue the call:
you are defining a CustomElement. If that CustomElement will utilize a #shadow-root then you are defining a WebComponent. A WebComponent uses slot elements within the #shadow-root for slotting (transposing) the innerHTML (DOM Nodes) contained within any HTML that has that tagName element.
What's the problem, why should I think about these things?
Given that background we now have the foundation to understand why wrap does not provide expected WebComponent #shadow-root semantics and behavior, but does allow self-contained CustomElement isolated $root wrappering of Vue VueComponent vueDefinitions.
Phew, sorry you had to read all that. It is a lot of pedantically crafted explanatory discussion for saying DOM
slotelements in aWebComponent'sshadowRootdon't work in Vue, and that you can't useWebComponents(defined by avueDefinition) properly in Vue without some modifications to Vue. Specifically, we need a way to tell Vue to treat appropriately markedslotelements, as DOMslotelements and not as Vueslotmacros.
Basically, Vue thinks that any slot elements it sees within a vue subtree of VNodes are slot elements that it should always use as its (Vue's) own macro element directives for performing transpositional replacement of innerHTML content into a render/template definition.
That behavior leads to a direct conflict with the DOM semantics of slot elements in #shadow-roots.
In other words Vue grabs all
slotelements wherever it sees them, which withwrapdefinedCustomElementsincludes their#shadow-root. Therefore the DOM never gets to see them in a wrappedWebComponents#shadow-rootand can't perform the expected DOM slotting semantics.