How did we choose between React and Vue


Introduction

  It’s been a while since our company decided to revise some major parts of our system. While a lot of effort has been directed towards improving the architecture’s modularity and refactor the parts related to our business logic, it has also been decided to rewrite the web interface of our system using some of the modern frameworks.

  Written using JSF (Java Server Faces), the web interface has to expose a lot of the functionalities to our customers, allowing them to track the information about the products in the warehouse as well as trigger different actions.

  Catching a glimpse on the current best practices and approaches, it was clear for us that moving away from the JSF would mean rewriting the UI so we wanted to make sure we choose the right option. We also had to be careful with our legacy. We wanted to make sure the choice would suit our customer’s needs, allowing us to implement any of the new features that would improve the user experience, as well as the overall look and feel.

Options

  When choosing among the available options we’ve decided to stop on the top 2 possibilities:

  • React
  • Vue.js

  I have to admit, even before starting exploring the options, our bet was that we would end up using React and we were actually very eager to work with it, especially with all the hype around it today. Plus it’s easy to fall for a library that’s backed up by a big player in the game.

  On the other hand, we already have several other web projects built with Vue and the feedback that we’ve gathered internally was very positive. Needless to say, we were a bit surprised to see that Vue is not that far behind in the ratings, considering that 2 years ago it was not even listed in the surveys. Long story short, we decided to give them a shot and see which one comes first.

Architecture

  Both frameworks rely on the component-based architecture, meaning that the web application should be broken down into multiple small reusable parts that have to be assembled. So no big difference here.

  With a little bit of research we’ve found that there are a lot of available libraries for practically anything you might want to build with these frameworks. For an objective choice, we decided to build two small but absolutely similar apps and make a decision upon the result. I’d like to share the result with you, in case you’re thinking of picking one of these frameworks for your next project.

  What did we compare:

  • the structure of the project
  • the syntax of components declaring
  • how to handle the data changes
  • how to pass data and enable communication between the components
  • handling mouse/keyboard events

Prototype

  We built from scratch something similar to the following mockup:


Pre-requisites

  Before starting to work with any of these frameworks, node.js and npm should be installed.

Creating the project

  Both libraries come up with special tools allowing creating the skeleton of the app from scratch.

  React has a special instrument called “create-react-app” and Vue has the “vue-cli”. These command line tools make sure the right folder structure is created and the necessary configuration is added.

React: 
npx create-react-app my-app

Vue.js: 
vue create my-app
  

  Verdict: vue-cli turned out to be more developer friendly allowing us to actually choose different configurations during the project generation.


Structure of the project

  The structure of the app is very similar for both projects, having few major things in common.


  There is a main JS file that bootstraps the web application and creates the necessary instances. Both React and Vue attach themselves to the DOM by rendering the main component into the HTML container with the given id.

React

Vue

  Vue uses HyperScript out of the box. React uses JSX. Both options define the way the frameworks render the HTML and construct the complex components structures with all the necessary event listeners. Simply put, in both Vue and React you link the HTML layout with the JS code. The main.js file contains the logic that initializes everything.

  Vue.js uses a shorthand for the createElement function, called h() which stands for hyperscript. This function creates the VirtualDOM, allowing Vue.js to keep track of the changes that happen and apply them on the real DOM. React also uses the virtual DOM, but it uses the ReactDOM helper to render it.

  Since React is more explicit here, it is easier to grasp it. However, we could not help but notice a major similarity at this point, which consists in the fact that there is a main component (MainPage) that is imported and passed to the render function.

  From this point forward the app is being built like this:


  There is a starting point and all the components are arranged in a hierarchy.

Component

  You might wonder how a component looks like?

React
export default class MainPage extends Component {
    constructor(props) {
        super(props);
        this.state = {};
    }

    /* methods */
    
    render() {
        return {/* HTML template goes here*/}
    }
}
  

  React’s components have to extend the base class “Component”. Few major things are very important at this point:

  1. Props are what you as a developer pass to the component when you render it. These arguments have to be specified when referencing the component in the render function of another component or inside Main.js.
  2. State is the object that keeps all the attributes that need to be watched for. Such attributes are called reactive. Simply put, if reactive attributes are referenced in the template, the changes are automatically reflected when the variables are updated.
  3. Render() function is called whenever the component needs to be displayed, i.e. the component is either rendered from the main.js or it is included in another component’s render() function.
  4. Any necessary method can be added to this class and referenced in the template:
export default class MainPage extends Component {
    add = () => {
        /* add new to do item in the list */
    }
    
    render() {
        return (
            <!-- omitted for brevity -->

            <button onClick={this.add}>Add</button>

            <!-- omitted for brevity -->
        )    
   }
}
  

  Please note the { } in the template allowing you to reference methods from the component, i.e. embed this.add into the HTML.

Vue

  Vue.js on the other hand generates the main component in a less familiar way. First of all, by using Vue-CLI you get the components generated in *.vue files, which have the following structure:

<template>
   <!-- HTML goes here -->
</template>

<script>
   <!-- JS or TypeScript go here -->
</script>

<style>
   <!-- Plain CSS or SASS go here -->
</style>
  

  This style is called “single file component” (SFC) and is encouraged within Vue.js projects as a way to standardize the way of declaring and using the components. Here’s how it looks like:

<template>
    <button v-on:click=”this.add”>Add</button>
    <!-- Here we reference the methods without {} -->
</template>

<script>
     export default {
        name: 'main-page',
        props:{
           /* props of the component should be defined here */
        },
        data() {
           return {
              /* the state goes here */
           }
        },
        methods: {
           add() {
             /* add new item in the list */
           }
        }
    }
</script>

<style>
/*  omitted */
</style>
   

  The object declared and exported inside the is what would be passed to the Vue.component constructor as: new Vue(‘main-page’, { /* declaration of the props, data and methods */}). When using SFC, this is handled by the compiler.

  Verdict: this small, yet still perceivable level of separation of concerns that SFC enables seemed quite appealing, especially for the newcomers, although SFC is not the only way of writing Vue components just like React also allows writing the code in JSX files.

Handling data

  For a simple to-do app, we’d need:

  • an array of existing tasks;
  • a temporary variable where to keep the new value;
  • when the user presses ‘Add’ the new value should be added to the list;
  • on remove, the value should be deleted.

  First of all we need to define the 2 variables: array and a string in the state of the app.

React
constructor(props) {
     super(props);

     this.state = {
          items: [
               { 'todo': 'Learn React' }
          ],
          todo: ''
     };
}
  
Vue
data() {
    return {
        items: [
            { todo: 'Learn Vue' }
        ],
        todo: ''
    }
}
  

  Having the necessary variables in place we could already proceed with displaying the list of to-do items.

  We wanted to have a separate component for each to-do item, thus each item would be responsible of its own state and displaying the list could potentially look like:

Displaying to-do’s
React
/* inside the MainPage component*/
render(){
    return (
        <p>List of to-do’s</p>
        {this.state.items.map((item, index) => {
                 return <ToDoItem
                           key={index}
                           todo={item.todo}
                           delete={this.remove.bind(this, index)}/>
                 }
           )}
    );
}
  
Vue
<template>
<p>List of to-do’s</p>
<ToDoItem v-for="(item, index) in list"
          :key="index"
          :todo="item.todo"
          v-on:delete="onRemove"/>
</template>
  

  To clear things up, ToDoItem is another component that would keep 2 things: the title of the to-do and the button allowing us to delete a certain item. Before we’ll see what the ToDoItem component looks like, there are few more things to explain:

  • The ‘todo’ attribute is the prop passed to the component. In this case this is the text of the to do.
  • The ‘key’ is a special attribute that helps React/Vue identify what’s been changed inside a list, causing the list to be re-rendered once elements are added to or removed from the list (a.k.a reactivity).
  • remove/delete is the attribute that allows specifying the function that should be executed every time the user removes a to-do item. Remember that the removal will be triggered in the ToDoItem, while the full list is kept inside the MainPage component.

  Here’s where the things get really different. In React we need to pass the function from the parent component to the child via:

delete={this.remove.bind(this, index)}
  

  In Vue, we make the parent component listen to the event of removing the to-do item via:

v-on:delete=”onRemove”
  

  The child component should notify the parent about the event and let the parent handle the logic. This is the paradigm that Vue uses in order to share data between the components. When sending the data from the parent to the child, use props. For the backwards operation use events.


The to-do component
React
export default class ToDoItem extends Component {
    constructor(props) {
        super(props);
    };

    render() {
        return (
          <p>{this.props.todo}</p>
          <button onClick={this.props.delete}>Delete</button>
        );
    }
}
  

  When the user will click the button to delete the to-do item, we’ll execute the delete function passed as a prop.

Vue
<template>
    <p>{{todo.text}}</p>
    <button @click="deleteItem(todo)">Delete</button>
</template>

<script>
    export default {
        props: ['todo'],
        methods: {
            deleteItem(todo) {
                this.$emit('delete', todo)
            }
        }
    }
</script>
  

  Since the list with all the to-do items is located in the parent component, we have to notify the parent about the removal. In opposition to React’s functions passed as a prop, Vue advocates another approach: emitting events.

  Verdict: The event-driven communication leads to loosely coupled components. In practice this means safer component replacement and easier integration.

Adding to-do’s

  Finally, one more interesting thing needed to be handled:


  For both apps we wanted to create a text input and let the user enter the to-do items.

React

  Here’s what we needed to add to accomplish this with React:

<input type=”text” 
       onChange={this.onChange} 
       onKeyPress={this.onInputKeyPress}/>

<button onClick={this.add}>Add</button>
  

  Where:

onChange = e => {
  this.setState({
       todo: e.target.value
  });
};
  

  and onInputKeyPress:

onInputKeyPress = e => {
    if (e.target.value !== '') {
        if (e.key === 'Enter') {
            this.add();
        }
    }
};
  

  In React, you need to call the setState() function in order to change the variables used inside the template. For this, it is necessary to listen for the onChange event and set the necessary variables.

  As in any other web apps, we wanted to make sure the to-do item is added on both enter keys and click on the add button.

Vue

  With Vue, the template is fairly similar:

<input type="text" 
       v-model="todo" 
       v-on:keyup.enter="createNewToDoItem"/>

<button v-on:click=”createNewToDoItem”>Add</button>

createNewToDoItem() {
    this.items.push({ text: this.todo});
    this.todo = '';
}
  

  In contrast with React, Vue has a special attribute v-model that sets the state automatically. Under the hood it works similarly with React, but the developer is not supposed to listen for the onChange events manually.

  Vue also provides a shortcut for the keyboard event handlers: v-on:keyup.enter which makes it easier to catch more specific keyboard events and not check the key type manually.

  Here’s a short summary of what has been described:

React Vue
Watching the changes Manual Listen for the onChange event and set the state via this.setState() v-model
Creating a component extend React.Component
  • Single file component
  • new Vue(name, {options})
Parent → Child communication props props
Child → Parent communication callback functions event bubbling events transmitted via this.$emit(‘event’, {options})
Mouse events onClick v-on:click or @click
Keyboard events shortcuts No Yes e.g. @keyup.enter, or @keyup.esc
TypeScript support Yes Yes
CLI tools create-react-app vue-cli
Framework size ~100KB ~80KB
Available libraries
  • Material Kit React
  • Material-UI
  • React Bootstrap
  • React Virtualized (render efficiently large datasets)
  • Evergreen
  • PrimeReact
full list
  • Vuetify
  • Qasar
  • Element
  • Vue material
  • Keen-UI
  • PrimeVue
full list

The verdict?

  We decided to go with Vue. Why?

  1. Easier data handling and events.
  2. Easier and less coupled communication between the components.
  3. Separation of concerns with SFC (single file component) allows easier development for newcomers.

  All this ensures a shallow learning curve. Apart from this, in order to ease the knowledge sharing between our teams, we decided to pick a technology already used for other internal projects.

Source code

  The source code for the projects is available on Github.

  • Vue.js – https://github.com/sscerbatiuc/vue-todo
  • React – https://github.com/sscerbatiuc/react-todo

References

  1. Vue-CLI reference – docs
  2. Creating new app with React
  3. Vue.js documentation
  4. React documentation
  5. Used images – https://www.monterail.com/hubfs/vuevsreact.jpg
Share this article:

Stanislav S.
Java Developer