Room with a Vue Part 3: Comparing Vue.js with Angular and React

Room with a Vue Part 3: Comparing Vue.js with Angular and React

By Zachary Klein, OCI Senior Software Engineer

February 2019

Vue.js

Introduction

In Part 1 and Part 2 in this series, we learned the fundamentals about the Vue.js library, as well as how to put them in action with a simple app. In this concluding article, we’re going to compare Vue.js with its primary rivals, Angular (2 and above) and React, and see how this relative newcomer to the front-end development scene stacks up. We will focus primarily on the developer experience when writing apps with these frameworks. All three offer excellent documentation and active development communities, and third-party training and online resources are also plentiful for each of the frameworks.

One topic that will not be considered is that of performance. Performance is a key priority of the development teams of all three frameworks, and comparisons of rendering speed and other performance metrics tend to become outdated very quickly. Often, the newest release of any of the frameworks outpaces the others. In reality, any of Vue, Angular, or React are likely to be more than fast enough for a modern web app, and bottlenecks in performance are more likely to be the configuration of the build or the business needs of the app. With that in mind, we won’t be making performance comparisons in this article, but for the latest statistics on these and more JavaScript frameworks, we recommend this website: https://stefankrause.net/js-frameworks-benchmark8/table.html

Comparison

Our comparison will follow the outline below:

Framework Goals

A helpful way to get a sense of these frameworks is to review their official taglines (as of early 2019).

The official Vue.js homepage starts with the following description: "Vue is a progressive framework for building user interfaces."

React’s official tagline (again, from the official website) is strikingly similar: "A JavaScript library for building user interfaces."

Angular's documentation describes the framework as "a platform that makes it easy to build applications with the web." (Emphasis added).

The above statements suggest that there is quite a bit of similarity in the goals for both React, and Vue – they are both focused primarily on the building of UIs. By contrast, Angular is better described as a platform, with a much broader feature set than either Vue or React, ranging from directives and templates on the UI side, to dependency-injection, routing, and handling of HTTP requests.

When it comes to building UIs however, all three of the frameworks have some things in common. React, Vue and Angular are component-oriented in their UI features. Components have lifecycles, can have state, and can include other components within their rendered content, whether that be a template (Angular and Vue) or a render method (React and, optionally, Vue).

When designing apps with any of these frameworks, you will tend to take a similar approach of dividing up the UI into smaller segments, building components for each one, and breaking those down into smaller components to keep them manageable. We will discuss specific differences in how components behave across the frameworks later in the article.

Templates

Vue is very pragmatic in its design choices. Templates are plain HTML, meaning that any existing HTML page can easily be converted into Vue components with no rewriting of the markup. By contrast, straight HTML cannot always be used as JSX in a React component. Some attribute names are different, and there are other subtle differences between the structure of JSX and HTML, although fortunately these are fairly few.

However, Vue also recognizes the power of render functions, and in fact uses them under the hood. This means that HTML templates are only one way of rendering content from a Vue component. You also have the option of using a render method, and even returning JSX as the component output if you have the proper build tooling set up.

Vue JSX Example

import MyComponent from './MyComponent.vue'
 
new Vue({
  el: '#app',
  render: function (h) {
    return (
      <MyComponent myValue={1}>
        <h1>Hello World!</h1>
      </MyComponent>
    )
  }
})

In practice, it's uncommon to find Vue apps that use JSX. However, the support is certainly there, and it can potentially make migration of React components to Vue components less painful (especially for developers that know and prefer JSX over HTML templates).

Data-binding

Another key factor to consider when comparing these three frameworks is how data from a component is passed to the template, and vice-versa. This process is known as data-binding. For example, given a simple component with a string property, you might render it within your template using the double-curly brace syntax.

Vue Simple Data-Binding Example

<h1>Welcome, {{ username }}</h1>

This sort of variable-binding directly from a component to a template (or within JSX) is supported by React, Angular, and Vue. However, the frameworks differ when it comes to how – or whether – they handle binding from the UI, to the component. This is common in components that render forms or form controls, where the UI elements allow the user to change the values or select from options. Typically you want those user changes to be reflected in the internal variables within the component. This type of data binding, where the component’s data is rendered to the UI and changes in the UI are mirrored to the component variable, is called two-way data-binding.

Angular Two-Way Data-Binding Example

 @Component({
  selector: 'my-component',
  template: `<input [(ngModel)]="username" required>`
  })
export class MyComponent {
  name: string = '';
}

Angular has made two-way data-binding popular since the framework’s conception, and the pattern does give a lot of power to developers. However, there are also downsides to two-way binding. It adds complexity to the re-rendering cycle of components, and it can make the UI less predictable, leading to difficult-to-diagnose bugs.

On the opposite end of the spectrum, React forces developers to work entirely with one-way data-binding, such as in the simple variable example shown above. This means that dynamic behavior in the UI (such as a form control) has to be passed down from a component in the form of a function. Rather than automatically sending changes from a UI element back to the component, React requires the developer to pass a function as an event-handler to the element, and that function can then make a change to the data within the components state. When the state has been updated, React triggers a re-render of the component, which will reflect the new value.

React One-Way Data-Binding Example

class MyComponent extends React.Component {
 
  state = { username: '' }
 
  updateUserName = event => this.setState({ username: event.target.value })
 
  render() {
    return <form><input type="text" onChange={updateUserName} value={this.state.username} /></form>
  }
}

Once again, Vue is very pragmatic in its approach to data-binding. Like React, Vue supports simple one-way data-binding of state variables to elements in the template with the v-bind directive (or the :[property] shorthand). Vue even handles the reactive rendering of the component in a very React-like way, using a Virtual DOM and a cascading createElement API (see the Vue docs on this topic).

Vue One-Way Data-Binding Example

<template>
  <input type="text" :value="username" @input="updateUserName" />
</template>
 
<script>
export default {
  data() {
    return { username: "" }
  },
  methods: {
    updateUserName(event) {
      this.username = event.target.value;
    }
  }
}
</script>

In addition, Vue borrows a page directly from Angular by providing seamless two-way data-binding with the v-model directive, which will synchronize the value in the component’s state (we’ll discuss state later) and the value in the template, updating either side of the relationship when there is a change.

Vue Two-Way Data-Binding Example

<template>
  <input type="text" v-model="username" />
</template>
 
<script>
export default {
  data() {
    return { username: "" }
  }
}
</script>

Components

All three of the frameworks under discussion feature a robust component model.

Components simply represent isolated elements of your app that can interact with each other in a hierarchy (or "component tree"), and can optionally hold their own data (or state). Component interactions vary between these frameworks, but typically a component will have some means of accepting input, and possibly emitting output to other components. Regardless of whether a component interacts with others, every component can render HTML output to the UI (either using a template or a render call that generates the markup).

Components also have a lifecycle, which varies by framework, but typically allows the developer to add custom behavior at strategic points along the life of a component (e.g., when a component is created or initialized, when a component is updated, and when a component is destroyed).

The ubiquity of components means that many frameworks have adopted similar conventions or features. Vue, Angular, and React components share all of the features described above. Some key differences remain, of course.

Component Definition

As discussed in the previous articles in this series, Vue components are typically defined using single file components, which is a Vue-specific format that allows you to express the template, state, logic, and styles for a component in a single .vue file.

Vue Single-File-Component Example

<template>
  <h1>Hello {{ name }}
</template>
<script>
  export default {
    data() {
      return { name: "World" }
    }
  }
</script>
<style scoped>
  h1 {
    color: blue
  }
</style>

Vue components can also be defined simply by initializing them with the Vue.component function and passing in the same options that you would otherwise include in the .vue file’s <script> section.

Vue.Component Example

Vue.component('hello-world', {
  data() {
    return {
      name: "World"
    }
  },
  template: '<h1>Hello {{name}}</h1>'
})

Finally, Vue supports a sort of "functional" (also known as "stateless") component, which is defined simply by including the option functional: true in the component’s instance definition object.

Functional components cannot use data (state) or lifecycle hooks, and are subject to additional limitations. However, they can be more performant than stateful components.

Vue Functional Component Example

Vue.component('hello-world', {
  functional: true,
  props: {
    name: String
  },
  template: '<h1>Hello {{name}}</h1>'
})

In React, components can be defined as either classes or functions, although the latter is often considered preferable. Like a Vue single-file-component, the "template" (actually rendered JSX output) is kept within the same file as the component definition and is essentially part of the component.

React Component Class Example

class HelloWorld extends React.Component {
 
  state = { name: "World" }
 
  render() {
    return <h1>Hello {this.state.name}</h1>
  }
}

As in Vue, functional components in React cannot make use of state or lifecycle methods. This will likely change in the near future with the introduction of hooks (see this SETT article by my colleague Mark Volkmann for an introduction to the hooks API). Unlike Vue, React’s functional components are actually written as "plain" JavaScript functions.

React Functional Component Example

const HelloWorld = {name} => <h1>Hello {name}</h1>

Angular components are almost always written as TypeScript classes, with the associated template and CSS each kept in separate, component-specific files.

Angular Component Example

@Component({
  selector: 'hello-world',
  templateUrl: './hello-world.html',
  styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
 
  constructor() { }
 
  ngOnInit() {
  }
}

Component Interaction

Generally speaking, all three of these frameworks support the same sort of "input" method for components. In Vue and React, a component can be passed "props", which syntactically resemble HTML element attributes, but can be used to pass in any type of data (or even a function) from a parent component into a child one.

Vue Props Example

<template>
  <MyComponent :name="'World'" />
</template>
 
<!-- MyComponent.vue -->
<script>
  export default {
    props: {
      name: String
    }
  }
</script>

Notice that by default, Vue provides for some measure of type-checking for props (although this is optional). React offers similar protection, but only through the use of an additional prop-types library, as seen in the example below.

React Props Example

import React, {Component} from 'react';
import {string} from 'prop-types';
 
class MyComponent extends Component {
static propTypes = { name: string };
 
render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
//usage...
<MyComponent name="World" />

Angular’s term for "props" is "input," and in an Angular component these are defined as class properties decorated with the @Input decorator.

Angular @Input Example

@Component({
  selector: 'hello-world',
  template: `
    <h1>Hello, {{name}}</h1>
  `
})
export class HelloWorldComponent {
  @Input() name: String;
}
 
<!-- usage -->
<hello-world [name]="World" />

However, Angular also includes a full-featured dependency-injection context, allowing components to call out to shared logic and/or state outside of the component scope. Neither React or Vue has such a concept, although that lack arguably makes for a simpler programming model.

Component State

An important feature of a component model is managing the internal data or "state" of the individual component. As opposed to inputs (e.g., "props" in Vue and React), state refers to data that is held within a component and cannot be shared (other than when explicitly passed as a prop) between other components.

Vue and React each treat state as a component-wide object (although React can accept any value as state ), with special properties and behavior. The most important of these behaviors is that a change to the state object (data in the case of Vue) will trigger a re-render of the component.

React differs from Vue in that the state object cannot be directly changed. Instead, the state is modified asynchronously by calling the setState method, which accepts an object containing the state change to be applied and performs a shallow merge between the object and the current state. This is done in order to ensure that React applies state changes reliably in conjunction with the component’s render cycle.

  state = { name: "World" }
 
  changeName = () => {
    this.setState({name: "Internet"})
  }

However, while with Vue one can write code such as below:

  data() {
    return { name: "World" }
  }
 
  //...
  methods: {
    changeName() {
      this.name = "Internet"
    }
  }

It turns out there is a bit of magic going on here. In fact, the code above is not accessing the data name property directly. When the component is instantiated, Vue creates "getter" and "setter" methods for each property in the data object, and calls to this.[property] are proxied to these getter/setter methods. Vue refers to this as making the component "reactive." This allows Vue to retain control over the state changes and rendering process, while making it easier for the developer to write readable code.

Angular has no concept of a "state" object. The state of each component can be thought of as the combined properties of that component’s class. They can be accessed and updated directly without any special API.

Event-Handling

Event-handling is a common requirement in JavaScript frameworks, and all three of these frameworks make it very easy to bind behavior to DOM events (such as onClick). In addition to DOM events, often you will want your own components to respond to changes in the app or trigger a notification so that another component can do the same. In Vue and Angular, components can trigger (or emit) their own events.

In Vue, the $emit helper can be used to trigger an event, and optionally pass arguments to any event handlers.

Vue Events Example

<template>
  <h1 @click="sendClicked">Hello World</h1>
</template>
<script>
export default {
  name: "hello-world",
  methods: {
    sendClicked() {
      this.$emit('clicked', 'Someone clicked me!')
    }
  }
}
</script>
 
<!-- usage -->
<hello-world @clicked="handleClick" />

Emitting events in Angular involves creation of an EventEmitter. The code is a bit more tedious than in Vue but follows a similar pattern.

Angular Events Example

@Component({
  selector: 'hello-world',
  template: `<h1 (click)="clicked.next('Someone clicked me!')">Hello World!</button>
`,
})
export class AddTodoComponent {
  @Output() clicked = new EventEmitter();
}
 
//usage (handleClick is a method in the parent component)
<hello-world (clicked)="handleClick($event)" />

React takes a different approach, where custom event-handling is done from the top down, by way of passing functions as props into a component. Instead of emitting an event, the component simply calls the "prop" function, which is essentially a callback. Again, highlighting Vue’s flexibility, this approach can be followed in Vue as well, as seen in the next examples.

React Function Prop Example

import React, {Component} from 'react';
import {func} from 'prop-types';
 
class HelloWorld extends Component {
  static propTypes = { clicked: func };
 
  handleClick = () => this.props.clicked('Someone clicked me');
 
  render() {
    return <h1 onClick={handleClick}>Hello, World</h1>;
  }
}
//usage (handleClick is a function in the parent component)
<HelloWorld clicked={handleClick} />

Vue Function Prop Example

<template>
  <h1 @click="såendClicked">Hello World</h1>
</template>
<script>
  name: "hello-world",
  export default {
    props: {
      clicked: Function
    },
    methods: {
      handleClick() {
        this.clicked("Someone clicked me")
      }
    }
  }
</script>
 
<!-- usage (handleClick is a function in the parent component) -->
<hello-world @clicked="handleClick">

Ecosystem

The ecosystems of all three of these frameworks are quite rich, with numerous plugins and third-party libraries available for all of them. It is difficult to quantify these additional libraries, of course, but let’s take a handful of common categories and review the most popular options available to each framework.

It should be noted that this section is by necessity somewhat subjective, as each of the libraries mentioned has its share of passionate proponents and detractors. The goal here is to introduce the most popular options and place them in context so that you can better navigate the greater ecosystem, not to declare a "winner" in any one category.

State Management

State management libraries typically provide a basic framework and API for storing and mutating the state across your app. State management enforces some rules about when and how state is stored and changed, and allows you to perform advanced techniques, such as "time travel debugging," stepping backwards through state changes in your app to see exactly what changes took place across your entire component hierarchy.

Vue provides an "official" state management library in Vuex. Vuex follows the Flux state management architecture pioneered by Facebook for React, with concepts such as stores (where state is stored), mutations (which change state), and actions (special methods that can perform asynchronous operations, such as Ajax calls, and then commit mutations). Vuex is well-integrated with Vue, as one would expect, including special support in Vue’s Devtools.

React does not have an official library for state management, but one of the most popular options is Redux, which is another Flux implementation. Redux uses similar terminology to Vuex, but the usage is quite different. In particular, Redux makes use of reducer functions. Reducer functions "calculate" state changes (described by actions), which are simple objects that describe the change to be performed. In Redux, the state in the store is considered immutable, so the reducer’s job is to calculate a brand new state that will replace the previous state (unlike Vuex, which actively mutates the store).

Redux is framework-agnostic, and can be used in any of the three frameworks we’re reviewing here. The Redux-React bindings are officially maintained by the Redux team, and so enjoy the best integration, as opposed to third-party bindings (which are available) for Vue and Angular.

Angular does not have as clear a choice for state management as React and Vue (the official documentation does not appear to address the topic), but the most popular library is NGRX.

NGRX is a Redux-implementation for Angular that leverages TypeScript and RxJS. However, Redux’s emphasis on functions may clash with the sensibilities developers accustomed to TypeScript and the class-oriented programming model. An alternative that seems to be gaining traction is Akita, which offers a more object-oriented approach to state management as opposed to a functional one.

In summary, Vue and React each benefit from more or less "official" state management solutions in Vuex and Redux, while Angular has a leading contender in NGRX but no official solution.

Routing

Routing allows single-page apps to behave as though they are not, in fact, running on a single page. In an app of any complexity, it will often be desirable to allow the user to navigate through the UI using forward/back controls, bookmark a specific view or "page" within the app, and be redirected from one portion of the app to another (for example, a login screen when a 401 response is received from an API server).

Vue makes this choice simple by offering an official routing library in Vue Router. Vue Router provides a near-exhaustive set of routing features, such as route parameters, wildcards, nested routes, and more. Routes are defined as an array of objects, which is passed to an instance of VueRouter, which in turn is passed to the app’s top-level Vue instance.

Vue Router Example

const routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login }
]

Vue Router requires a place to actually render the routed components, which is typically the main content section of your app. You must indicate where this rendering will happen using the <router-view> component.

Vue Router Example (2)

div>
  <router-view></router-view>
</div>

From within a routed component, you can access properties of the route via the $route helper, as well as programmatically navigate to new or past routes with the $router helper.

Vue Router Example (3)

export default {
  computed: {
    userId() {
      return this.$route.params.id
    }
  },
  methods: {
    redirectLogin() {
      this.$router.push('/login')
    }
  }
}

React does not have an official routing solution, but the dominant library is the battle-tested React Router.

Similar to Vue Router in many respects, this library takes a component-centric approach to building and navigating routes (instead of a config-first approach, where the routes are defined separately from the components). This approach might seem novel for some developers, but it does fit nicely with React’s emphasis on JSX as a programming approach.

React Router Example

const App = () => (
  <Router>
    <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/login/">Login</Link>
          </li>
        </ul>
 
      <Route path="/" exact component={Home} />
      <Route path="/login/" component={Login} />
    </div>
  </Router>
);

Angular integrates routing into the framework with a dedicated Router and RouterModule. Routes are defined on a per-module basis, using a configuration map that is akin to that of Vue Router.

Angular Routing Example

const routes: Routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login }
];
 
@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  // ...
})
export class AppModule { }

Like Vue Router, you must indicate where the routed components will be rendered using a special directive, <router-outlet>.

Angular Routing Example (2)

<h1>Welcome</h1>
<ul>
  <li><a routerLink="/">Home</a></li>
  <li><a routerLink="/login">Login</a></li>
</ul>
<router-outlet></router-outlet>

Since Angular takes care of routing as part of the framework, there’s little uncertainty as to which library you should use when building your Angular app. However, while there are more options available to React and Vue developers, there is still a clearly popular choice for both of those frameworks as well, with React Router and Vue Router, respectively.

Ajax

This will be a short section, because neither React nor Vue provides an official solution for making HTTP requests from your apps. This makes sense given their respective framework goals. which both name "building user interfaces" as their primary aim. Making Ajax calls isn’t directly a part of UI, although in reality almost every UI will require such requests. However, the development teams for both React and Vue have decided that HTTP features should be handled by dedicated APIs and/or libraries, and not a core feature of the frameworks themselves.

Fortunately, two of the most popular standalone HTTP libraries require almost no effort to use with React or Vue. These are axios and fetch.

Axios is a well-designed HTTP client that works both in browsers and from server-side node.js apps. Aside from basic HTTP calls, axios supports advanced features such as interceptors, which allow you to add custom behavior (including terminating and redirecting) before a request is made or before the response is returned to the calling code.

Axios Example

// Within an async function ...
try {
  const response = await axios.get('/api/info');
  // Do something with response.
} catch (error) {
  console.error(error);
}

Fetch is different from Axios in that it is technically an API, not a library (although it sometimes requires a library to be used, depending on your browser). Fetch is somewhat more low-level than Axios, and doesn’t offer the same breadth of features, but it has the advantage of being a core API available in modern browsers, meaning that no additional tooling is required to use it (with the caveat that for older browsers, a polyfill is required).

Fetch Example

// Within an async function ...
try {
  const response = await fetch('/api/info');
  // method defaults to GET
  const data = await response.json();
  // Do something with data.
} catch (error) {
  console.error(error);
}

Angular, which bills itself as a "platform" for web applications, includes a dedicated HTTP client that supports many advanced features, such as interceptors, caching, and progress reporting for requests. The client is designed to take advantage of Angular’s reliance on TypeScript and RxJS conventions, and the Angular documentation encourages developers to keep all HTTP logic within dedicated services.

Testing

The many testing tools and frameworks for JavaScript apps make up a rich ecosystem, and JavaScript frameworks rarely provide their own dedicated testing framework. The two main types of tests written for JavaScript web apps are unit tests, which test the execution of individual components and methods, and end-to-end tests (aka e2e tests), which render the app, interact with the app as a user would, and then make assertions about the resulting content.

For the purposes of this comparison, we will simply highlight the default test stack that's provisioned by each framework’s official CLI/project generator.

Vue’s CLI provides several options for unit and e2e tests.

  • For unit tests, you have the choice of either the Mocha testing framework (preconfigured with the Chai assertion library) or the more modern Jest framework (which does not require a separate library for assertions).
  • For e2e tests, the choices are Nightwatch (a Selenium-based testing tool) and Cypress (a pure-JS e2e testing suite).

React’s create-react-app project generator uses Jest as the default testing stack. This isn’t surprising, given that Jest and React are both developed by Facebook, and Jest suits React’s focus on simplicity with its zero-config approach and integrated assertion and mocking support. No e2e support is included with create-react-app, but it's common to use Cypress for end-to-end testing of React applications, and configuring it is easy.

The Angular CLI generates configuration for the Jasmine testing framework for unit tests and Protractor (which uses Selenium under the hood) for e2e tests.

Devtools

All three of the frameworks we are reviewing provide specialized browser plugins that give insight and visibility to the running app, and sometimes the ability to dynamically interact with your components as they are running in your browser. These plugins, also known as "devtools," are powerful tools both for debugging and for learning about the inner-workings of your favorite framework.

Developed and maintained by the core Vue.js team, the Vue Devtools are available as Chrome and Firefox plugins, as well as a standalone Electron app. The plugin allows you to browse your component hierarchy, manipulate the internal data of components, interact with your Vuex-managed state, and track events emitted from components.

Figure 1. Vue Devtools

Figure 1. Vue Devtools

React also has an official devtools solution, aptly named React Devtools. Again, there is a plugin available for Chrome and Firefox, and a standalone app as well. The devtools make it easy to browse and play with your React components, although there is no built-in support for state-management tools such as Redux. However, official devtools are available for Redux as well.

Figure 2. React Devtools

Figure 2. React Devtools

Angular’s devtool plugin is named Augury, is available for Chrome and Firefox, and provides much the same feature set as the React and Vue devtools. You can directly edit the properties of components, view component source code, and set breakpoints. Augury also allows you to view and interact with the routes within your Angular app, something that the React and Vue devtools don’t support as of yet.

Figure 3. Augury Devtools for Angular

Figure 3. Augury Devtools for Angular

Adoption

Vue made some news in June 2018 when it surpassed React in the number of "stars" on Github. Github stars are one method, although imperfect, to gauge developer interest in a framework, and it certainly is significant that a young framework such as Vue has already surpassed a much more established rival in this count.

Figure 4. Github stars: React vs. Vue vs. Angular

Figure 4. Github stars: React vs Vue vs Angular via https://timqian.com/star-history/#facebook/react&vuejs/vue&angular/angular

However, it is important to put the "stars" of Vue into perspective. While Vue certainly has captured developer interest, React's and Angular’s history means they have a much larger base of existing projects using the frameworks and developers who are trained and familiar with them. This is reflected in the NPM package download statistics, which show Vue lagging well behind Angular and leagues below React’s dominant position.

Figure 5. NPM Weekly Download Trends

Figure 5. NPM Weekly Download Trends (past 6 months) via https://www.npmtrends.com/@angular/core-vs-react-vs-vue

Surveys of developers are hit/miss when it comes to identifying trends in the industry, but they can still be helpful in giving a general sense of what frameworks are gaining or falling behind year over year. The Jetbrains Dev Ecosystem Survey, for example, reported that in 2017 developers reported usage of the "big three" frameworks at the following percentages:

Table 1. 2017 JetBrains Dev Ecosystem Survey

ReactAngularVue

49%

66%*

20%

  • Angular + AngularJS results

In 2018, the same survey indicated some sizable shifts.

Table 2. 2018 JetBrains Dev Ecosystem Survey

ReactAngularVue

60%

41%*

33%

  • Angular + AngularJS results

Finally, the results for the most recent "State of JS Survey" seem to concur with the numbers above.

Table 3. 2018 State of JS Survey

 React (vs 2016)Angular (vs 2016)Vue (vs 2016)

Never heard of it

0.1% (0.5%)

0.1% (1.2%)

1.3% (27.1%)

Heard of it, not interested

9.2% (11.5%)

31.8% (39.1%)

20.5% (30.7%)

Heard of it, would like to learn

19.1% (35.3%)

10.4% (39.7%)

46.6% (32.2%)

Used it, would not use again

6.7% (3.9%)

33.8% (6.4%)

2.8% (1.3%)

Used it, would use again

64.8% (48.7%)

23.9% (13.6%)

28.8% (8.7%)

While it’s not possible to draw completely objective conclusions from this data, it seems fair to say that Vue’s adoption rate is likely growing as fast, if not faster, than React's (which would seem inevitable at some point, given React’s near-saturation of the "market"). Angular adoption appears to be in a steady decline, by comparison. And it’s worth noting that Vue is the clear leader in the "would like to learn" category of the State of JS results (and a happy loser of the "would not use again" prize). Time will tell how much of that interest converts into actual adoption, but the momentum suggests that Vue will remain a real contender in the frontend space for a while yet.

Conclusion

There’s no such thing as a "best" framework, in part because every application is different, and because every developer is different. The diversity in JavaScript frameworks today can be a mixed blessing, but it stems from different, but legitimate, perspectives and ideas about how to structure and design apps for the modern web. Fortunately, the frameworks reviewed here have a lot of common ground, such as component-oriented design and component lifecycles. These shared concepts help when evaluating the different frameworks, and also mean that learning one framework is likely to help you appreciate, or at least understand, another.

Ultimately the best framework for your app and your team is going to be the one that makes sense to your developers, with a programming model that makes sense to you and makes it easy to reason about your business needs.

Angular’s robust platform approach and rich feature-set might appeal to enterprise developers who aren’t bothered by a healthy dose of boilerplate and added syntax, in return for advanced features built right into the framework. JavaScript aficionados and fans of functional programming might appreciate React’s simplicity, declarative style, and minimal framework magic. And developers who are more pragmatic about their programming model, or who perhaps are new to frontend development and value a gentler learning curve, just might find a perfect medium in Vue.js.

References

Software Engineering Tech Trends (SETT) is a monthly publication featuring emerging trends in software engineering.

Read More from Zachary Klein

secret