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

HTML case sensitivity workaround #2308

Closed
yyx990803 opened this issue Feb 6, 2016 · 48 comments
Closed

HTML case sensitivity workaround #2308

yyx990803 opened this issue Feb 6, 2016 · 48 comments

Comments

@yyx990803
Copy link
Member

The Problem

So as we all know, HTML is case insensitive. myProp="123" gets parsed as myprop="123" and this has led to the caveat in Vue.js where you have to use my-prop="123" to refer to a prop declared in JavaScript as myProp. This bites beginners quite often.

In addition, we also need to apply the same mapping to custom components - e.g. when you define a component:

import MyComponent from './my-component'

export default {
  components: {
    MyComponent // es2015 shorhand
  }
}

You must use <my-component> in the template instead of <MyComponent>.

The annoying part here is because Vue.js relies on the browser to pre-parse the templates, by the time Vue.js gets to compile it, the case information is already lost.

The Idea

What if we adjust the matching logic so that things would just work? For example, making this possible:

<MyComponent :myProp="msg"></MyComponent>

Why?

In addition to eliminating the camelCase vs kebab-case inconsistency in our code, there are a few practical reasons why we would prefer PascalCase/camelCase for components and props:

  1. Props need to be referenced in templates and JavaScript as properties. Having hyphens in them makes it very awkward. (myProp vs. this['my-prop'])
  2. When we import another component, the variable name cannot be kebab case. e.g. You can do import MyComp from './my-comp' but my-comp is simply not a valid variable name. And with the ES2015 object literal shorthand you can just do components: { MyComp }.
  3. Capitalized component names stand out more vs. regular elements, making it clearer what tags are custom components and what tags are not.

Technical Details

The underlying implementation is that when we process the props and component options, we normalize them into lowercase. This way, they simply become mycomponent and myprop during internal matching process, but you can still use the desired case in your app code. (In fact users don't even need to know about these internals)

Potential concerns:

  1. Backwards compatibility. The lowercase conversion can be done alongside the current kebab-case conversion, so both syntax can co-exist, nothing will break.

  2. myProp and MyProp will be treated as the same thing in the template. However, it doesn't make any sense to have two props or two components in the same component differentiated only by case, and we can easily detect and warn against such usage.

  3. Should we apply the same rule to custom events as well? For example:

    <MyComponent @myEvent="handleIt"></MyComponent>

    This basically means event names become case-insensitive, which has a bigger implication than props and component names because this affects event system usage in pure javascript. Does it make sense to normalize all event names into lowercase? Again, it seems rare to have two events differentiated only by case (e.g. having both myEvent and myevent in the same app that do different things), but I do want to get feedback on this.

@finico
Copy link

finico commented Feb 6, 2016

<MyComponent :myProp="msg"></MyComponent>

I often want to write that way to see difference between the components and tags

@johnRivs
Copy link

johnRivs commented Feb 6, 2016

+1

@azamat-sharapov
Copy link

Does it make sense to normalize all event names into lowercase?

Yes! It makes sense, because it makes code more readable. I always keep event names lowercase, separate them by dash, instead of camelcase. I think adding warning for camelcase event names would be also good.

@jamesxv7
Copy link

jamesxv7 commented Feb 6, 2016

Does this means that Vuejs will be moving away from the HTML spec pursing a similar approach like Angular?

@aXenom
Copy link

aXenom commented Feb 6, 2016

Is there some performance concern?

@Teevio
Copy link

Teevio commented Feb 6, 2016

A couple thoughts on the potential concerns:

  1. As long as backwards compatibility exists I'll be cool with whatever is ultimately decided, but will probably be continuing using the kebab case method
  2. The lack of distinction between camel-case and lower camel-case could be confusing for some. Generally speaking var CamelCase would refer to a class and var camelCase would refer to a non-class variable, var camelCase = new CamelCase();. But, I don't think this would be an issue, because you wouldn't want to be creating classes that were named after your components.
  3. I would agree that creating two unique events only distinguished by the case would be very poor programming.

My biggest concern is introducing weird inconsistencies with how people code. For example, all of these are valid and identical: :myprop="" :myProp="" :mYpRoP="" :my-prop="".

@sirlancelot
Copy link

👎 Keep kebab-case in the markup and camel case in the code. It's part of the HTML spec and ignoring case will be a greater learning curve for those coming from other frameworks or who have already learned the standard.

@utopiaio
Copy link

utopiaio commented Feb 6, 2016

I agree with @Teevio, consistency will be lost

HTML uses kebab-case & it's an accepted community standard that ECMAScript is a camelCase language. We should keep them separate instead of hiding (In react the only way you can get react to render custom attribute is via data-* & aria-*, which enforces consistency).

Explaining why (say via MDN link or even here) camelCase <-> kebab-case to a beginner will greatly help the beginner's HTML understanding.

@Rastishka
Copy link

Agree with Evan, readability and code consistency is more important!
+1

@jonagoldman
Copy link

To me it will look really weird to have camelCase inside HTML.

HTML is HTML, JS is JS

@ghost
Copy link

ghost commented Feb 6, 2016

current version is just fine

@Mat-Moo
Copy link

Mat-Moo commented Feb 6, 2016

Having been using Vue for 6 months, that - still gets me everytime :( not a big deal as the warning for Vue are so good I know what I did, but I can fully understand the idea here, and support it +1

@tiagolr
Copy link

tiagolr commented Feb 6, 2016

+1 Backwards compatibility too.

@kazupon
Copy link
Member

kazupon commented Feb 7, 2016

+1
I agreed. we need to keep the backwards compatibility.

Does it make sense to normalize all event names into lowercase?

I agree with @azamat-sharapov

@yyx990803
Copy link
Member Author

@Teevio Vue components are roughly classes: when you do var MyComp = Vue.extend({ .. }) you can then do var myComp = new MyComp(). As for the multiple valid syntax issue, it already exists: :my-prop, :MY-PROP and :mY-pRop all work the same as of now, because HTML simply throws away all case information. It's not much different with the proposed feature. Like with all style arguments, it's all about picking a style and stick with it.

Re @jamesxv7 @Moe-Szyslak @jonagoldman and others that have concerns about moving away from HTML standard: it's totally valid to write camelCase tags/attributes in HTML, it's just the tag/attribute name matching will be performed in a case-insensitive manner. This is what the spec says:

In documents in the HTML syntax:

Tag names for HTML elements may be written with any mix of lowercase and uppercase letters that are a case-insensitive match for the names of the elements given in the HTML elements section of this document; that is, tag names are case-insensitive.
Attribute names for HTML elements may be written with any mix of lowercase and uppercase letters that are a case-insensitive match for the names of the attributes given in the HTML elements section of this document; that is, attribute names are case-insensitive.

So, if using PascalCase/camelCase improves code consistency/readability, it's totally spec-compliant to do so. This may not be for everyone, but if you prefer kebab-case you can keep using that.

And in particular, re @jamesxv7: this is different from what Angular 2 is doing. Angular 2 is making their templates case-sensitive by introducing a customized HTML parser. On the contrary Vue actually follows the spec by making the JS counterparts case-insensitive.

@patrickfatrick
Copy link

I prefer keeping kebab in html. I like the idea of consistency but I like the idea of spec compliance more.

Found camelCase tag: . HTML is case-insensitive. Use instead. Vue will automatically match it against components defined with camelCase ids in JavaScript.

This warning is already pretty concise as to how component registration works. That said, as others have mentioned, I think allowing camelCase in HTML is great, as long there's still the option to continue writing kebab.

@Teevio
Copy link

Teevio commented Feb 7, 2016

@yyx990803 yah, agreed. Just trying to think up as many arguments as I could against it, but honestly I don't have any that would stick. Like you mentioned, at the end of the day we're arguing stylistic choices. I personally think that as long as we can stick with what we already have while having the option to use the new stuff (but not forced to) I'm cool with the changes mentioned.

@smolinari
Copy link

If kebab-case still works and using camelCase/ PascalCase are an option that don't break BC, when I use them, then I can't be against the addition. It isn't a change that forces me to do something different. It's just a new option.

The only thing I could say or suggest is to make sure this option is well documented and - Good work, as always Evan!

Scott

@paulpflug
Copy link

Maybe we can make an option out of it: warning or ignoring.
Say I have a component: myComponent
and I refer to it in html as mycompOnent I personally would prefer a warning, like:
we found something that would match "myComponent": "mycompOnent" (line 152), use "my-component" instead
The same for props of course.
I think later-on-readabilty is more important than very thing working on the first try.

I also stumbled upon kebab-case/camelCase problems. But the real problem was I got no warnings what was wrong ;)

Default could be that there is no warning and it just works, that's irrelevant for me.
Also the warnings should be visible only in debug mode I think

@simplesmiler
Copy link
Member

What about things like atone vs a-tone vs at-one? I imagine they are quite rare occurrences though.

@yyx990803
Copy link
Member Author

@simplesmiler kebab case props would still be matched with case-sensitivity using old rules.

@guidobouman
Copy link
Contributor

This doesn't promote transparency to web standards. The custom elements spec states that names should contain a hypen: <my-component/>

What about what @simplesmiler said: addOne and adDone would execute the same code. This would be especially nasty for the implementation for events,

And because html is case insensitive, lets not introduce the idea of casing from the library. This implementation would only promote casing in html, which in my opinion is a bad idea.

Also, we use hypen separation in file names. Should we remove them there as well, and start adding casing?

Lastly: having the hypen & casing system co-exist promotes different coding styles for new developers on a team.

I prefer @paulpflug's approach; Proper warnings in this area would help a lot.

@blake-newman
Copy link
Member

I'm not a fan of making the HTML Pascal/Camel case. It breaks web standards, I know it's nice to keep consistency.

By trying to make things the tiniest bit consistent you add another layer of complexity. It may also be enticing bad practices. A library should promote staying to the standards and not mislead developers, as one day they may have to work in a place not using Vue, resulting in not understanding why the HTML is being parsed differently.

I totally agree with @paulpflug: Adding a warning means less work for production code and puts developers back on track to writing valid code.

@blake-newman
Copy link
Member

A good argument case as to why this shouldn't be implemented: http://eisenbergeffect.bluespire.com/on-angular-2-and-html/

This is commonly a highlighted reason people dislike Angular 2. I agree totally with keeping libraries conforming to standards. It was once drafted for HTML to be case sensitive, and it was thrown out because of too many issues and opening up situations of too much flexibility.

@jamesxv7
Copy link

jamesxv7 commented Feb 8, 2016

@blake-newman: Regarding this, I think @yyx990803 already talk about it in a previous comment.

@patrickfatrick
Copy link

@jamesxv7 That comment sums it up pretty well; Evan is not proposing changing HTML spec, he's proposing changing how Vue locates component names. Rather than convert kebab to camel and finding the matching component it would probably strip dashes (to accommodate kebab) and then search components with no case sensitivity. The HTML itself will continue to be spec compliant. It also would allow us to use whatever case we want. This seems like not an evil or bad choice to me :)

@simplesmiler
Copy link
Member

@yyx990803 do you plan the <MyComponent> style to be the promoted style (i.e. docs and examples will be written like this), or it will be just an option, and kebab-case style will remain the primary one?

@yyx990803
Copy link
Member Author

@blake-newman do read this comment - it does conform to the standard :)

@paulpflug @guidobouman : there already are warnings for camelCase tags and attributes if you are using latest versions of vue-loader or vueify. However, the camelCase checks must be performed at compile time because at runtime the case information would have already been lost due to the HTML parser behavior. So if you are using Vue without vue-loader or vueify, there won't (and can't) be any warnings.

@smolinari
Copy link

@yyx990803 - But, the spec @blake-newman linked to for web components does state this:

The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production, must contain a U+002D HYPHEN-MINUS character, and must not contain any uppercase ASCII letters.

I am just not too sure how that relates to Vue components. In the docs, you do say, you try to loosely follow the web components standard.

You may have noticed that Vue.js components are very similar to Custom Elements, which is part of the Web Components Spec. In fact, Vue.js’ component syntax is loosely modeled after the spec.

So, I'd say the spec needs to change first, in order to allow camelCase and PascalCase.

Scott

@Teevio
Copy link

Teevio commented Feb 8, 2016

@smolinari the Vue docs say that it is 'loosely modeled' not 'strictly' and in my mind that leaves room for this change.

@paulpflug
Copy link

@yyx990803 the case information may be lost, but there could still be a warning.
When I wrote 'mycOmponent' in the template it will be parsed to mycomponent but expected is my-component then Vue (in debug mode) should look for mycomponent besides my-component and warn me about the wrong usage. The lost case information doesn't matter here.
There could be an option to suppress the warning and match directly instead (equals your suggested behavior).

@rpkilby
Copy link

rpkilby commented Feb 8, 2016

-1 to migrating to camelCase/PascalCase. It would be somewhat jarring to see JS-like syntax in HTML. Same reason why I cannot stand jsx.
+1 to @paulpflug's suggestion. If the problem is onboarding beginners, why not just issue a warning that informs the user of the problem?

@yyx990803
Copy link
Member Author

@paulpflug that does sound like a valid idea!

@simplesmiler
Copy link
Member

I agree, having a warning that says 'mycomponent' is missing, did you mean 'my-component'? feels better than silent substitution.

@finico
Copy link

finico commented Feb 8, 2016

@yyx990803 Is it possible to do this on a global option api?
eg
Vue.config.kebab = true (by default) -> <my-component :my-prop="msg"></my-component>
Vue.config.kebab = false -> <MyComponent :myProp="msg"></MyComponent>

@prog-rajkamal
Copy link

@yyx990803
just wondering, what is the ideal we are striving for?
as @rpkilby said, <MyComponent myCustomProp="myProp" data-name="prop" aria-label="Close" onclick="myDialog.close()"> looks weird.

Essentially, the problem exists because js and html are different technologies and use different naming systems. And using same case(kebab or camel) in both technologies will shift weirdness from one place to another but the underlying problem will persist
So I believe, best we can do is draw a line. and the current line i,e. kebab case in html context and camleCase (and PascalCase) in js context is very good.

so IMO, we should just support current conventions instead of looking for a better one. And ofcourse, use warning to help beginners

@yyx990803
Copy link
Member Author

@prog-rajkamal yeah, I'm now inclined to just implement the warning.

@smolinari
Copy link

I now vote 👍 on just adding the warning too.

Scott

@sirlancelot
Copy link

👍 for adding a warning

@ThePeach
Copy link

👍 for the warning too

flytreeleft pushed a commit to flytreeleft/vue that referenced this issue Feb 27, 2016
@yyx990803
Copy link
Member Author

Closed via ccf9bed & d7b55c4

@anthonygore
Copy link

anthonygore commented Jan 16, 2017

A warning would be great. I just spent an hour trying to figure out why my custom event wasn't be responded to (the answer being it had a camel cased name).

@guidobouman
Copy link
Contributor

There's a warning when you have a corresponding component or prop that would respond to the kebab-case version of your PascalCase component or prop. If you make a typo in an event there's not much Vue can do about that.

Or do you mean for default existing event props like v-on:keyup or @keyup in short?

@anthonygore
Copy link

anthonygore commented Jan 16, 2017

@guidobouman in my template file I had <my-component v-on:customEvent="myMethod">. In a child component I had this.$emit('customEvent');. The actual event being listened to by the parent is customevent not customEvent, of course, but it took me ages to figure that out because it's not easy to debug. I was thinking it would have been good to warn that a camel case attribute would not be parsed as such, for forgetful folk like me. Perhaps this has already been discussed above, and if so I apologize.

@simplesmiler
Copy link
Member

@anthonygore unfortunately it's impossible, because browser converts html to lowercase before Vue has a chance to access it.

@MattCCC
Copy link

MattCCC commented Oct 13, 2017

My question is, why does Vue cannot convert it for us? Why do we have to remember about kebab-case in in vue files? It makes things more awkward for beginners... I just wasted last 20 mins for that...

@sirlancelot
Copy link

sirlancelot commented Oct 13, 2017

The one rule to commit to memory: HTML = kebab-case, JavaScript = camelCase

By HTML I mean attributes and tags. Attribute values are JavaScript expressions when you use v-bind, so the second statement applies.

@cwhsu1984
Copy link

My question is, why does Vue cannot convert it for us? Why do we have to remember about kebab-case in in vue files? It makes things more awkward for beginners... I just wasted last 20 mins for that...

wasted last 1.5hr because I didn't just google it... dxmn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests