Attributes are lost when using native customElements.
Vue version
3.5.13
Link to minimal reproduction
https://github.com/LoveVin/vue3-issue-demo
Steps to reproduce
I need to customize a web component. It has been working fine in previous Vue 2 projects, but I’m encountering issues when running it in a Vue 3 project.
After troubleshooting, I found that if I assign a value to this.name within the constructor of the custom element class, the name attribute is lost when the custom component is rendered on the page. The source code is <template> <aa-bb name="123"></aa-bb> </template>, but in the browser, it renders as <aa-bb data-v-7a7a37b1></aa-bb>, with the name attribute missing. This does not happen in Vue 2 projects.
The issue can be reproduced with the vue2-demo and vue3-demo created using Vite. You can find the compressed packages below. After downloading and installing them, the issue can be replicated.
I have also verified that the same issue occurs with Vue CLI. Using CDN to load Vue does not have this issue, nor does a native project. Therefore, I suspect it might be related to the Vue single-file component template packages.
Because this is a TypeScript project, when defining class properties, it’s necessary to initialize them within the constructor. As a result, the properties that are initialized later cannot be found.
What is expected?
<aa-bb name="123" data-v-7a7a37b1></aa-bb>
What is actually happening?
<aa-bb data-v-7a7a37b1></aa-bb>
System Info
System:
OS: macOS 13.5 arm64 Apple M1 Pro
Binaries:
Node: 20.18.0
npmPackages:
vue: ^3.5.13 => 3.5.13
Any additional comments?
Why is this happening? Is it a bug in some of the Vue 3 compilation packages? Can it be fixed?
use the ^ prefix to set the property as an attribute.
<aa-bb ^name="111" age="22"></aa-bb>
see Playground
or remove this.name = ''
see Playground
There is an inconsistency in attribute handling between Vue 2 and Vue 3. Vue 2 uses setAttribute(key, value), while Vue 3 uses this[key] = value if the element defines a key (key in el). When the attribute is prefixed with ^, setAttribute will be used.
@edison1105 "Why should I change the way native custom tags are used, and it's unreasonable to ask users to add a ^ prefix when assigning attributes to custom tags within a Vue3 project. You mentioned that this is an issue with Vue3's handling method. Is this a bug in Vue3? Vue3 should not affect the definition and usage of native custom tags."
For custom elements, I think current modern frameworks share a common sense of assigning the data as property other than attribute, if that property exists on the element. I can see React does this too for your demo We can also check each framework's behavior details in Custom Elements Everywhere too
I think it's author's responsiblity to reflect property to attribute in custom elements if they want to use both property and attribute
I think it's author's responsiblity to reflect property to attribute in custom elements if they want to use both property and attribute
While it's the custom element author's responsibility to handle how IDL and content attributes reflect each other in the CE, there can be no argument about whether it's legitimate that custom elements can have both IDL and content attributes and have both use completely different reflection behavior.
HTML is the precedent for this: there are content attributes that reflect their IDL attributes (e.g. id, name on form controls) but generally content attributes don't reflect IDL attributes. It's rather more common that IDL attributes reflect content attributes.
In other words: it's completely up to a custom element to define its interface and that includes whether things are configurable via content and/or IDL attributes. Vue must be able to support both (and it does) and I also agree that setting name="test" should set the content attribute of a CE, not its IDL attribute (even if no content attribute should be used by the CE as a CE can't even really define its content attributes explicitly). If anything, it should be the IDL attribute setting in templates that should have a special syntax: after all, HTML has that very syntax for setting content attributes and no syntax at all for setting IDL attributes.
@edison1105 Where is the carrot syntax in mentioned in the docs?
Found it! The caret is actually a shorthand for the .attr modifier. https://vuejs.org/api/built-in-directives.html#v-bind
Found it! The caret is actually a shorthand for the
.attrmodifier. https://vuejs.org/api/built-in-directives.html#v-bind
Actually, I don't think that's accurate. It seems that the ^ prefix doesn't treat the value as an expression (therefore, not using v-bind), but the .attr suffix does.