Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The component name is not strict, resulting in the inability to run the VUE program #4422

Closed
qq974969638 opened this issue Aug 23, 2021 · 2 comments · Fixed by #4429
Closed

Comments

@qq974969638
Copy link

qq974969638 commented Aug 23, 2021

Version

3.2.4

Reproduction link

https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuICA8aDE+e3sgbXNnIH19PC9oMT5cbiAgIDx6ai3mtYvor5XkuIA+PC96ai3mtYvor5XkuIA+XG4gICA8emot5rWL6K+V5LqMPjwvemot5rWL6K+V5LqMPlxuPC90ZW1wbGF0ZT5cblxuPHNjcmlwdCBzZXR1cD5cbmNvbnN0IG1zZyA9ICdIZWxsbyBXb3JsZCEnXG48L3NjcmlwdD4ifQ==

Steps to reproduce

  1. Create a new index.html
  2. 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>

  1. After saving, open the index.html in the browser
  2. 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

  1. Reason: I used a special html element tag name.

  2. 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.

  1. 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>

  1. 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 ####################

@qq974969638
Copy link
Author

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

@shadowings-zy
Copy link
Contributor

@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

yyx990803 pushed a commit that referenced this issue Aug 24, 2021
@github-actions github-actions bot locked and limited conversation to collaborators Oct 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants