Vue's Single File Components vs. React's JSX


Vue and ReactI have to admit, my first experiences with React several years ago were meet with a lot of cynicism. The idea of mixing JavaScript and Markup made me clutch my pearls to the core.

Now I know that you can use React without using JSX, but I think that is missing the point in a lot of ways. Of course, I'm not a React expert so I am sure you'll let me know how wrong I am ; )

Ok, What is JSX?

In case you haven't used React yet, let me just talk about how JSX works. In React, when you build a component, you're mixing JavaScript with markup. The idea is that during compilation, the markup is replaced with DOM code to build up the markup you're specifying. For example:

import React from "react";

class CallList extends React.Component {

  constructor(props) {
    super(props);
    this.store = props.store;
    this.state = {
      calls: this.store.calls
    };

    this.store.onChange(calls => {
      this.setState({ calls: this.store.calls });
    });
  }

  render() {
    return (
      <div className="row">
        <div className="col-10 offset-1">
          <h3>Call Log</h3>
          <table className="table table-striped table-bordered">
            <thead>
              <tr>
                <th>From</th>
                <th>Email</th>
                <th>Call Time</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {this.state.calls.map((c) => 
                (<tr key={c.id}>
                  <td>{c.name}</td>
                  <td>{c.email}</td>
                  <td>{c.callTime}</td>
                  <td />
                </tr>)
              )}
            </tbody>
          </table>
        </div>
      </div >
    );
  }
}

export default CallList;

In the render method, you can see that we have a mix of JavaScript and markup. This isn't that different from how Angular or Vue handle it, but it being inline with the JavaScript code file is what gives me the cringes. It's supporters talk about the markup and code being close to each other and that will make it more maintainable. In addition, since no parsing is happening at runtime, it should be faster too.

It's detractors say that this makes it easy to mix responsibilities. My chief issue with it is about responsibilities but also that the markup isn't HTML. It's JSX's special HTML. Because it's inside of JavaScript, there are rules about how it is mixed in. The first one most people run into is having to use "className" instead of "class" in elements because of the keyword issue. But how you add brackets and parentheses to the markup is a bit non-intuitive as well.

Let's look at something similar with Vue.

In Comes Vue.js

In the meantime, I've become enamored with Vue.js. For those who haven't read other articles of mine, I love how easy it is to use Vue without the ceremony of transpilation or other build steps, though in practice I use Webpack and babel (or TypeScript) when I'm building something larger than a quick demo.

One of the benefits of going to a full-built step in Vue is the use of Single-File Components. Single-File Components are simply a file that has three sections: Markup (e.g. HTML), Code (e.g. JavaScript or TypeScript), and Styling (e.g. CSS). For example:

<template>
  <div>
    <div><router-link to="/" class="btn btn-sm btn-secondary">Back</router-link></div>
    <table class="table">
      <tr>
        <td>Name</td>
        <td>{{ call.name }}</td>
      </tr>
      <tr>
        <td>Email</td>
        <td>{{ call.email }}</td>
      </tr>
      <tr>
        <td>Name</td>
        <td>{{ call.callTime | date }}</td>
      </tr>
      <tr>
        <td>Problem</td>
        <td>{{ call.problem }}</td>
      </tr>
      <tr>
        <td colspan="2"><button class="btn btn-success" @click="answer(call)">Marked Answered</button></td>
      </tr>
    </table>
  </div>
</template>
<script>
  import { mapActions } from "vuex";
  import store from "../store";
  export default {
    store,
    props: ["call"],
    methods: {
      ...mapActions([ "answer" ])
    }
  }
</script>
<style>
  .someClass {
    font-size: 14px;
  }
</style>

So this is a different approach than JSX, but it has some of the same benefits and problems. Though since they're all in their own sections, at least I know that it's pure markup, code and styling. My question, is why don't I hate Single File Components?

What's the Difference?

I'm not perfect. I'm not even always consistent. But I am not sure this is one of those occasions. Separation of Concerns is something I am passionate about. I spent much of my early developer life working on Visual Basic monolithic apps. Having paid the cost of tightly coupled systems, I continue to rail against it. So if we look at both of these technologies, how do they fare with this in mind?

Single File Components

I have a preference for Vue in general, so take this with a grain of salt. What I like about how Vue's Single File Components is that they are completely optional. In fact, they only work if you're using a preprocessor (e.g. Webpack). So when you're learning Vue, you get how the framework does it way before you're shown that you have the option of using Single File Components.

Additionally, Single File Components are using a format that looks and feels like HTML. The different sections of the file are like they'd be on a traditional web page. You can take CSS and HTML (as well as other versions of those languages), and drop them in a Single File Component and it just works.

React

In React, the mixing of JavaScript/TypeScript and the markup means that it's too easy to mix the concerns. Letting you drop in Script in the middle of larger blocks of markup makes it harder not easier to read (IMO). The problem here isn't that it can't be separate or testable, it's that it's too easy to fall back into just making it work by mixing concerns.

As with most technologies, I don't want the wrong thing to be the obvious thing to do. React can certainly be separated into clean concerns. And you can certainly do React without JSX at all, it's just not the easy thing to do. Asking developers to start to learn with JSX, and they re-teaching them to ignore the way they learned to do it 'correctly' makes me nervous. I'd love to see more React instruction focusing on what Reacts does right (lots of small, stateless components) instead of pretending that the benefit of React is that it's like old-school ASP or JSP style development.

Conclusion

Unless you skipped the article and just want to know what I think, you already know what I'm going to say. I am still not a fan of JSX. It feels too easy to mix the metaphors of presentation and business logic.

Even with Vue, I think that many apps won't even bother with Single File Components (especially if they start without a preprocessor). Like any technique, a little goes a long way.

At the end of the day, I wouldn't choose React or Vue (or Angular) based on either of these technologies. You'd choose them because they better match to your requirements. Remember that Vue was built for agile client-side development; React was built for websites made of hundreds of small components. They don't match every type of application easily.

So, if you were expecting me to tell you that X framework is better than Y framework, this isn't the article for you. Please play nice in the comments!



Shawn
Shawn Wildermuth
Author, Teacher, and Coach




My Courses

Wilder Minds Training
Vue.js by Example
Bootstrap 4 by Example
Intro to Font Awesome 5 (Free Course)
Pluralsight
Designing RESTful Web APIs (new)
Building an API with ASP.NET Web API
Building an API with ASP.NET Core
Building a Web App with ASP.NET Core, MVC6, EF Core, Bootstrap and Angular
Less: Getting Started

Application Name WilderBlog Environment Name Production
Application Ver v4.0.30319 Runtime Framework x86
App Path D:\home\site\wwwroot\ Runtime Version .NET Core 3.0.0
Operating System Microsoft Windows 10.0.14393 Runtime Arch X86