Vue’s Darkest Day by Daniel Elkington

Translator’s Note: Originally written on 21 June 2019

Today, I was surprised to find the normally positive and friendly VueJS community locked in a fierce war. Two weeks ago, Vue creator Yu Yu Creek released a Request for Advice (RFC) for writing Vue components in a function-based manner in the upcoming Vue 3.0 release. Today, a critical Reddit post and similar critical comments on Hacker News have caused a flood of developers to express their anger at the original RFC, some of it insulting. It has been claimed in many places that:

  • All Vue code has to be rewritten in a whole new way because the existing syntax is being removed and replaced by something else;
  • All the time people spend learning Vue is wasted because everything changes;
  • The new syntax is worse than the old one because it has no mandatory structure and leads to spaghetti-like code;
  • The Vue core team suddenly implemented a hugely disruptive change without any consultation;
  • Vue is going to React!
  • No, Vue is becoming AngularJS/Angular!
  • All the HTML has to be written in one super-long string!

Looking at the pile of negative comments on Reddit, you might be surprised to find that the RFC of Yur Creek received a much higher percentage of positive emojis than negative ones, and many of the early comments were quite positive. In fact, the first comment was full of praise.

I was the first one to write a review. I happened to get a notification of the new RFC, read it right away, saw that this was exactly what I wanted from Vue 3.0, and that it would help me tremendously, so I left my first comment 15 minutes after the RFC was released to express my thanks. I’d like to expand here on why I think the new proposal is such a good idea, but first, to address some of the criticisms.

I suspect a lot of people get a little worked up after reading a post on Hacker News or Reddit with a lot of misleading comments and expressing their anger without reading the original proposal. Rainy Creek has updated the proposal to respond to a lot of people’s questions with a Q&A, in general:

  • If you don’t want to rewrite any code, then you don’t need to – the new syntax is additional, and it will still work in Vue 3.0 as long as the old syntax is still widely used. Even if it is eventually removed from the core code, the old syntax can easily be made 100% valid by plug-ins.
  • Time spent learning Vue isn’t wasted — the new component syntax uses the same concepts you spent time learning, and other concepts like single-file components, templates, and Scoped style do exactly the same.
  • Without consultation, there will be no changeRFCIs in theconsulting. The new syntax is still a long way from release.
  • No, the HTML code doesn’t need to be written into a super-long string.

A more subjective point is that the new syntax is inferior to the old syntax and results in less structured code. I wanted to give a simple example of why I was so excited to see the RFC, and why I felt it was better and would lead to better structured code.

Consider the interesting component that allows users to enter pet details. Please note:

  • A message displays when they finish typing in their pet’s name;
  • Another message is displayed after they select the size of their pet.

You can try out a demo of the components here or see the code written using Vue2. x (in Components /Vue2.vue) here.

Consider the JavaScript for 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; }}};Copy the code

Essentially, we have some data, properties calculated from that data, and methods for manipulating that data. Note that there is no way to put related things together in Vue 2.x. We cannot place the petName data declaration next to the petNameComment calculation property or the onPetNameBlur method, because in Vue 2.x, these options are organized by type.

Of course, for small examples like this, it doesn’t matter too much. But imagine a larger example that has a lot of functionality that requires Data, computed, methods, and even a Watcher or two. There’s no good way to put related things together yet! One might use something like mixins or higher-order components, but they all have problems — it’s hard to tell where a property comes from, and namespace conflicts. (Yes, splitting into multiple components is possible in this case, but this similar example is not.)

Instead of organizing components by type of option, the new proposal allows us to organize components by actual functionality. This is similar to the way you organize your personal files on your computer – you usually don’t have a “forms” folder and a “Word Documents” folder, instead you might have a “work” folder and a “vacation plans” folder. Imagine writing components using the syntax in the proposal (do my best, let me know if you see any bugs) :

import { state, computed } from "vue";
export default {
  setup() {
    // Pet name
    const petNameState = state({ name: "".touched: false });
    const petNameComment = computed((a)= > {
      if (petNameState.touched) {
        return "Hello " + petNameState.name;
      }
      return null;
    });
    const onPetNameBlur = (a)= > {
      petNameState.touched = true;
    };

    // Pet size
    const petSizeState = state({ size: "".touched: false });
    const petSizeComment = computed((a)= > {
      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 = (a)= > {
      petSizeState.touched = true;
    };

    // All properties we can bind to in our template
    return {
      petName: petNameState.name,
      petNameComment,
      onPetNameBlur,
      petSize: petSizeState.size, petSizeComment, onPetSizeChange }; }};Copy the code

Note:

  • It’s easy to put related things together;
  • By looking at the return value of the setup function, we can easily know what variables can be retrieved from the template;
  • We can even avoid exposing internal states that the template does not need to be touched.

In addition, the new syntax can have full Typescript support, which is difficult to achieve in Vue 2.x object-based syntax. And we can easily extract reusable logic into reusable functions. Such as:

import { state, computed } from "vue";

function usePetName() {
  const petNameState = state({ name: "".touched: false });
  const petNameComment = computed((a)= > {
    if (petNameState.touched) {
      return "Hello " + petNameState.name;
    }
    return null;
  });
  const onPetNameBlur = (a)= > {
    petNameState.touched = true;
  };
  return {
    petName: petNameState.name,
    petNameComment,
    onPetNameBlur
  };
}

function usePetSize() {
  const petSizeState = state({ size: "".touched: false });
  const petSizeComment = computed((a)= > {
    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 = (a)= > {
    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 }; }};Copy the code

In Vue 2.x, I often found myself writing a “monster component” that was hard to break down into smaller parts — it couldn’t break down into other components because many transactions were based on a small number of states. However, using the syntax in the proposal, it’s easy to see how the logic of a large component can be broken down into smaller reusable parts, which can be 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 seems so. The community that had been united in the direction of the project was split. But MY hope is that people will revisit this proposal, and it doesn’t break anything. They can still organize options by type if they want, but they can do more — cleaner code, cleaner code, more interesting libraries, and better Typescript support.

Finally, when using open source software, it’s good to remember that you can use it for free only because of a lot of effort put into it by the maintainers. Some of the overcriticism today is more than they deserve. The good news is that these rude criticisms are in the minority (though quite a lot), and most people can express themselves in a more polite way.

Updated on 23 June 2019:

I wrote the original text very quickly, not expecting it toTo get that kind of attention. Then I realized that the code example was too complicated for the point I was trying to make, so I simplified it a lot. The original code sample is inhere.