The component name is not strict, resulting in the inability to run the VUE program
Version
3.2.4
Reproduction link
Steps to reproduce
- Create a new index.html
- Paste the mold code
<!DOCTYPE html>
<body>
<div id="hello-vue" class="demo">
{{ message }}
<zj-测试一></zj-测试一>
<zj-测试二></zj-测试二>
</div>
</body>
<script src="https://unpkg.com/vue@next"></script>
<script>
const HelloVueApp = { data() { return { message: 'Hello Vue!!' } } };
Vue.createApp(HelloVueApp).mount('#hello-vue');
</script>
</html>
- After saving, open the index.html in the browser
- F12 view the log and get an error.
Uncaught SyntaxError: Identifier 'component_zj___' has already been declared
What is expected?
Vue should ignore invalid tags, and say "Hello Vue!!"
What is actually happening?
VUE cannot run
This is a demonstration with special characters that can be run: https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuICA8aDE+e3sgbXNnIH19PC9oMT5cbiAgIDx6ai3mtYvor5XkuIA+PC96ai3mtYvor5XkuIA+XG48L3RlbXBsYXRlPlxuXG48c2NyaXB0IHNldHVwPlxuY29uc3QgbXNnID0gJ0hlbGxvIFdvcmxkISdcbjwvc2NyaXB0PiJ9
Reason: I used a special html element tag name.
VUE uses the following code when converting element tag names.
#################
function toValidAssetId(name, type) {
return
${type}${name.replace(/[^\w]/g, '_')};
}
#################
Which resulted in:
The element tag "<zj-测试一>" is compiled into "component_zj" The element tag "<zj-测试二>" is compiled into "component_zj"
Therefore, a variable name conflict occurred internally.
- However, if I only keep a specific html element tag name, it works fine.
<!DOCTYPE html>
<body>
<div id="hello-vue" class="demo">
{{ message }}
<zj-测试一></zj-测试一>
</div>
</body>
<script src="https://unpkg.com/vue@next"></script>
<script>
const HelloVueApp = { data() { return { message: 'Hello Vue!!' } } };
Vue.createApp(HelloVueApp).mount('#hello-vue');
</script>
</html>
- I know that my element names are not standardized. But there is a problem here. When VUE encounters an unknown tag that cannot be processed, should it be sent to the DOM for the browser to process, or should it be deleted? How to explain, it can still run when only a special element name tag is reserved.
############## This is a special character code that can be run ####################
<!DOCTYPE html>
<body>
<div id="hello-vue" class="demo">
{{ message }}
<zj-测试一></zj-测试一>
</div>
</body>
<script src="https://unpkg.com/vue@next"></script>
<script>
const HelloVueApp = { data() { return { message: 'Hello Vue!!' } } };
Vue.createApp(HelloVueApp).mount('#hello-vue');
</script>
</html>
############## This is a special character code that can be run ####################
I think there is a solution. Due to my limited technical ability, I don't know if this repair will cause other problems. Here I only provide my suggestions for your reference, thank you.
The original function:
function toValidAssetId(name, type) {
return
${type}${name.replace(/[^\w]/g, '_')};
}
Rewritten as:
let element_name_map = new Map()
let element_inc = 0
function toValidAssetId(name, type) {
let retname = ""
let tempkey = name + type
if (element_name_map.has(tempkey)) {
retname = element_name_map.get(tempkey)
} else {
element_inc++;
retname = "_" + type + "_" + element_inc
element_name_map.set(tempkey, retname)
}
return retname
}
Let's take the following code as an example:
<template>
<h1>{{ msg }}</h1>
<a-b></a-b>
<a.b></a.b>
</template>
<script setup>
const msg = 'Hello World!'
</script>
The original function:
function toValidAssetId(name, type) {
return
${type}${name.replace(/[^\w]/g, '_')};
}
Convert "a-b" to "a_b" Convert "a.b" to "a_b" //This caused Vue SFC to generate the following code
return (_ctx, _cache) => {
const _component_a_b = _resolveComponent("a-b")
const _component_a_b = _resolveComponent("a.b")
//Error: duplicate variable name
Using the function I repaired, the following code will be generated:
return (_ctx, _cache) => {
const _component_1 = _resolveComponent("a-b")
const _component_2 = _resolveComponent("a.b")
Please note: "a-b" "a.b" are all html element names that conform to the w3c standard: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
A valid custom element name is a sequence of characters name that meets all of the following requirements:
name must match the PotentialCustomElementName production:
PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)* PCENChar ::= "-" | "." | [0-9] | "_" | [a-z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] This uses the EBNF notation from the XML specification. [XML]
name must not be any of the following:
annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph
@qq974969638 I think we don't need using Map
to store the relation of name
and element_inc
.
I think we can solve this edge case by adding an unique identifier on specific validAssetId
.
export function toValidAssetId(
name: string,
type: 'component' | 'directive' | 'filter'
): string {
// see issue#4422, we need add identifier if `name` has specific character
if (/[^\w-]/g.test(name)) {
let identifier = ''
for (let i = 0; i < name.length; i++) {
identifier += name.charCodeAt(i)
}
return `_${type}_${name.replace(/[^\w]/g, '_')}_${identifier}`
}
return `_${type}_${name.replace(/[^\w]/g, '_')}`
}
PR #4429