core icon indicating copy to clipboard operation
core copied to clipboard

Attributes are lost when using native customElements.

Open LoveVin opened this issue 1 year ago • 7 comments

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.

demo.zip

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?

LoveVin avatar Dec 20 '24 07:12 LoveVin

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 avatar Dec 23 '24 01:12 edison1105

@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."

LoveVin avatar Dec 23 '24 07:12 LoveVin

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

lejunyang avatar Dec 24 '24 09:12 lejunyang

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.

kleinfreund avatar Dec 24 '24 12:12 kleinfreund

@edison1105 Where is the carrot syntax in mentioned in the docs?

roydukkey avatar Oct 27 '25 19:10 roydukkey

Found it! The caret is actually a shorthand for the .attr modifier. https://vuejs.org/api/built-in-directives.html#v-bind

roydukkey avatar Oct 31 '25 18:10 roydukkey

Found it! The caret is actually a shorthand for the .attr modifier. 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.

roydukkey avatar Dec 01 '25 20:12 roydukkey