Today I was amazed to see the usually positive and friendly VueJS community descend into a bitter war. Two weeks ago Vue creator Evan You released a Request for Comment (RFC) for a new function-based way of writing Vue components in the upcoming Vue 3.0. Today a critical Reddit thread followed by similarly critical comments in a Hacker News thread caused a flood of developers to flock to the original RFC to voice their outrage, some of which were borderline abusive. It was claimed in various places that

All Vue code would have to be rewritten in a totally new way because the existing syntax was being removed and replaced with something else;

All the time people had spent learning Vue had been wasted given everything was about to change;

The new syntax was worse than the old, did not enforce structure, and would lead to spaghetti code;

The Vue Core team had suddenly implemented a huge breaking change without any consultation;

Vue is turning into React!

No, Vue is turning into AngularJS/Angular!

All HTML now needs to be written as a giant string!

With walls of negative comments on the Reddit Thread one may be surprised to discover on going to the RFC page that You's RFC has an overwhelmingly high ratio of positive to negative emoji reactions, and many of the initial comments were quite positive. Indeed, the very first comment is particularly full of praise.

I was the person who wrote that first comment. I happened to get a notification that there was a new RFC, read it straight away, saw that it was just what I wanted from Vue 3.0 and that it would help immensely, and left the first comment within 15 minutes of the RFC being published to express my gratitude. I hope to expand here on why I think the new proposal is such a great idea, but first, to address some of the criticism.

I suspect that many people got a little worked up after reading the Hacker News or Reddit threads which had some somewhat misleading comments, and voiced their outrage without reading the original proposal. Evan You has now updated the proposal with a Q&A that addresses many of the issues people have, but to summarise,

You don't need to rewrite any code if you don't want to - the new syntax is additive, and the old syntax will remain valid throughout Vue 3.0 and as long as it is still widely used. Even if it eventually gets removed from the Core code, plugins could easily allow the old syntax to be still 100% valid.

Time spent learning Vue was not wasted - the new component syntax uses the same concepts that you spent time learning, and other concepts such as Single File Components, templates, and scoped styles work exactly the same.

A change hasn't been made without consultation - the RFC is the consultation. The new syntax is still a long way from being released.

the consultation. The new syntax is still a long way from being released. And no, HTML code doesn't need to be written as a giant string.

A slightly more subjective point is that the new syntax is inferior to the old, and will lead to less structured code. I hope to demonstrate with a simple example why I got so excited when I saw the RFC, and why I think it is superior and will lead to better structured code.

Consider the following fun component that allows a user to enter details of their pet. Note that

A message gets displayed when they finish typing their pet's name;

Another message gets displayed after they select their pet's size.

You can try out a demo of the component here and can view the full code using Vue 2.x here (see components/Vue2.vue).

Consider the JavaScript of this component:



export default { data () { return { petName : "" , petNameTouched : false , petSize : "" , petSizeTouched : false }; }, computed : { petNameComment : function () { if ( this . petNameTouched ) { return " Hello " + this . petName ; } return null ; }, petSizeComment : function () { if ( this . petSizeTouched ) { switch ( this . petSize ) { case " Small " : return " I can barely see your pet! " ; case " Medium " : return " Your pet is pretty average. " ; case " Large " : return " Wow, your pet is huge! " ; default : return null ; } } return null ; } }, methods : { onPetNameBlur : function () { this . petNameTouched = true ; }, onPetSizeChange : function () { this . petSizeTouched = true ; } } };

Essentially we have some data, properties computed off that data, and methods that manipulate that data. And notice that in Vue 2.x there is no way to keep related things together. We can't keep the petName data declaration next to the petNameComment computed property or the onPetNameBlur method because in Vue 2.x things are grouped by type.

Of course this doesn't matter too much for a small example like this. But imagine a bigger example, that had multiple pieces of functionality that needed data , computed , methods , and even a watcher or two. There's currently no good way to keep related things together! One might use something like Mixins or Higher Order Components but these have issues - it's hard to see where properties are coming from and there are problems with namespace clashing. (And yes, in this case it would be possible to split things into multiple components, but consider this similar example where it isn't.)

Rather than organising components by option type, the new proposal allows us to organise components by actual functionality. It's similar to how you organise your personal files on your computer - you usually don't have a 'spreadsheets' folder and a 'word documents' folder, instead you might have a 'work' folder and a 'holiday plans' folder. Consider the above component written in the proposed syntax (as best as I am able to without seeing the output - let me know of any bugs you see!):



import { state , computed } from " vue " ; export default { setup () { // Pet name const petNameState = state ({ name : "" , touched : false }); const petNameComment = computed (() => { if ( petNameState . touched ) { return " Hello " + petNameState . name ; } return null ; }); const onPetNameBlur = () => { petNameState . touched = true ; }; // Pet size const petSizeState = state ({ size : "" , touched : false }); const petSizeComment = computed (() => { if ( petSizeState . touched ) { switch ( this . petSize ) { case " Small " : return " I can barely see your pet! " ; case " Medium " : return " Your pet is pretty average. " ; case " Large " : return " Wow, your pet is huge! " ; default : return null ; } } return null ; }); const onPetSizeChange = () => { petSizeState . touched = true ; }; // All properties we can bind to in our template return { petName : petNameState . name , petNameComment , onPetNameBlur , petSize : petSizeState . size , petSizeComment , onPetSizeChange }; } };

Note that

It's ridiculously easy to group related things together;

By looking at what gets returned by the setup function we can easily see what we have access to in our template;

We can even avoid exposing internal state ('touched') that the template doesn't need access to.

On top of that, the new syntax easily allows full TypeScript support which was difficult to achieve in the Vue 2.x object-based syntax. And we can easily extract out reusable logic into reusable functions. Something like



import { state , computed } from " vue " ; function usePetName () { const petNameState = state ({ name : "" , touched : false }); const petNameComment = computed (() => { if ( petNameState . touched ) { return " Hello " + petNameState . name ; } return null ; }); const onPetNameBlur = () => { petNameState . touched = true ; }; return { petName : petNameState . name , petNameComment , onPetNameBlur }; } function usePetSize () { const petSizeState = state ({ size : "" , touched : false }); const petSizeComment = computed (() => { if ( petSizeState . touched ) { switch ( this . petSize ) { case " Small " : return " I can barely see your pet! " ; case " Medium " : return " Your pet is pretty average. " ; case " Large " : return " Wow, your pet is huge! " ; default : return null ; } } return null ; }); const onPetSizeChange = () => { petSizeState . touched = true ; }; return { petSize : petSizeState . size , petSizeComment , onPetSizeChange }; } export default { setup () { const { petName , petNameComment , onPetNameBlur } = usePetName (); const { petSize , petSizeComment , onPetSizeChange } = usePetSize (); return { petName , petNameComment , onPetNameBlur , petSize , petSizeComment , onPetSizeChange }; } };

In Vue 2.x I often find myself writing a "monster component" that is hard to break up into smaller pieces - it can't be decomposed into other components because there is too much happening based on a small amount of state. However using the proposed syntax it's easy to see how big components could have logic broken up into smaller reusable pieces, moved into separate files if necessary, leaving you with small, easy-to-understand functions and components.

Is this Vue's darkest day so far? It looks like it. What was until now a community mostly united behind the project's direction has splintered. But I have hope that people will take another look at a proposal that doesn't break anything, still allows them to group things by option type if that's what they like doing, but allows for so much more - clearer code, cleaner code, more interesting library possibilities, and full TypeScript support.

Finally, when using open source software, it's good to remember that the maintainers are putting a lot of effort into something that you get to use for free. Some of the borderline abusive criticism seen today is something that they really shouldn't have to put up with. Thankfully the disrespectful comments were a minority (albeit a sizeable one) and many were able to express themselves in a more respectful manner.

Update June 23 2019:

I wrote the original post very quickly and without expecting it to receive the attention that it has. Since then I've realised that the code example was too complex for the point I was trying to illustrate, so I've simplified it greatly. The original code sample can be found here.