Learning Vue from React
Learning Vue from React
Picking up a new framework or library can seem intimidating. The good news is that ReactĀ andĀ VueĀ are two popular, well-supported tools for front-end development, and if you already know one, you can leverage your knowledge to become productive with the other relatively quickly.
Both are JavaScript-based, use a virtual DOM, and use composable, reactive components to build applications. They both have CLIs that take care of the boilerplate required to spin up an application quickly and easily. This blog is written with React developers learning Vue in mind, but if you are a Vue developer learning React, stick around. Many of the big, practical differences between the two come down to syntax and knowing one of the two can be easily leveraged into learning the other.
By exploring two applications, one written in React and one written in Vue, that output identical content in the browser, compare how they accomplish the same goal (both were scaffolded with their respective CLIs,Ā Create React AppĀ andĀ Vue CLI). Both applications make a request to an API that returns an array of music genres that will be rendered into a list of cards. You can find repositories for the applications below if you want to clone them and experiment, or you can just read along with the code snippets. Both applications are admittedly heavy-handed solutions to the problem presented but should give enough context to understand the basics of both technologies.
App
The main entry point for both applications is theĀ <App />
Ā component. Comparing the React and VueĀ <App />
Ā components, it may not be instantly clear that React uses JSX to create component structure, while Vue uses HTML. This will become more apparent as you move along.
What does stand out is that not only does the Vue component import the child components it needs, it also registers them in the exported object in the script tag. If you are a React developer and forget to do this, you will get a big, nasty error. Vue registering components this way allows child components that do not need updating to avoid a re-render.
// App.js
import React from "react";
import GenreList from "./GenreList";
import "./App.css";
const App = () => (
<div id="app">
<h1>Genres</h1>
<GenreList />
</div>
);
export default App;
// App.vue
<template>
<div id="app">
<h1>Genres</h1>
<GenreList />
</div>
</template>
<script>
import GenreList from "@/components/GenreList.vue";
export default {
name: "App",
components: {
GenreList
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
By convention, Vue also provides functionality identical to what something like styled-componentsĀ provides for React. By adding theĀ scoped
Ā attribute to the style tag in a Vue component, a unique ID is added to the component that limits the styles to that component only. Both CRA and Vue CLI allow simple setup for using SCSS instead of CSS.
GenreList
<GenreList />
Ā does the heavy lifting of the application, by making your API call, managing the state of the application, and rendering a list of genre cards. Both applications import an identicalĀ genreService
function that provides the data for the component. Each application sets the response to their internal state management and then iterates over the array of genre objects to render genre cards.
In the React version, you make the API call inside of a functional component with theĀ useEffect
Ā hook (this could also be accomplished in a class component with theĀ componentDidMount
Ā lifecycle method), and update state with the response data using theĀ useState
Ā hook (in a class component, you would callĀ setState
Ā on a successful response). You can thenĀ map
Ā over the genres array inĀ state
Ā and render aĀ <GenreCard />
for each genre.
// GenreList.js
const GenreList = () => {
const [genres, setGenres] = useState([]);
useEffect(() => {
genreService
.getGenres()
.then((response) => {
setGenres(response.data);
})
.catch((error) => console.log("There was an error", error));
}, []);
return (
<div className="genre-list">
{genres.map((genre) => (
<GenreCard genre={genre} key={genre.id} />
))}
</div>
);
};
Vue 2 doesn’t have the concept of functional versus class components (although Vue 3 will). InĀ GenreList.vue
, you use theĀ mounted
Ā lifecycle method to make your API call, and you store it inĀ data
, which is analogous to ReactĀ state
. In yourĀ data
Ā description, you giveĀ genres
a default value of an empty array so that you don’t have any problems when iterating over genres before the API call resolves.
// GenreList.vue
<template>
<div class="genre-list">
<GenreCard v-for="genre in genres" :key="genre.id" :genre="genre" />
</div>
</template>
<script>
import GenreCard from "@/components/GenreCard.vue";
import genreService from "@/services/genreService";
export default {
components: {
GenreCard
},
data() {
return {
genres: []
};
},
mounted() {
genreService
.getGenres()
.then(res => (this.genres = res.data))
.catch(error => console.log("There was an error", error));
}
};
</script>
<style lang="css" scoped>
.genre-list {
padding: 0 15%;
}
</style>
The syntax for iterating over a list in Vue is a bit different than React. Since you are using HTML instead of JSX, you don’t have access to the full power of JavaScript in your template, so Vue uses directives, or special attributes in the markup, that act as instructions to Vue on what to do to the DOM. In this case, you use the v-for
Ā directive to tellĀ <GenreList />
Ā to render a component (or HTML element) for every element in a list. Additionally, you can’t use curly brackets to pass props to child components in Vue, so you will need to useĀ v-bind
directives to pass props to a child component. This can be written as:
<li v-for="item in items" v-bind:key="item.id" v-bind:item="item">
or
<li v-for="item in items" :key="item.id" :item="item">
Both do the same thing, the second is just a shorthand syntax.
GenreCard
The last component to review in this application isĀ <GenreCard />
, which takes a genre object as a prop and renders the data from that object.
In React,Ā <GenreCard />
is a functional component that destructures props on the way in and renders a simple card with a genre and a description.
// GenreCard.js
const GenreCard = ({ genre }) => {
return (
<div className="genre-card">
<h4>{genre.genre}</h4>
<p>{genre.description}</p>
</div>
);
};
export default GenreCard;
The Vue implementation ofĀ <GenreCard />
Ā is similar, although you are required to describe the props the component expects to receive. This is very similar to React’sĀ prop-types
, although it is not optional in Vue. Vue also uses double curly brackets, called “mustaches,” to inject data into a template, as opposed to React’s single set of curly brackets.
// GenreList.vue
<template>
<div class="genre-card">
<h4>{{ genre.genre }}</h4>
<p>{{ genre.description }}</p>
</div>
</template>
<script>
export default {
props: {
genre: Object,
},
};
</script>
<style lang="css" scoped>
.genre-card {
padding: 20px;
margin-bottom: 20px;
border: 2px solid black;
}
</style>
Wrapping up
If you’ve been using React or Vue in your development, hopefully this blog has encouraged you to try something new that you aren’t familiar with. Differences are to be expected, but these two technologies are very similar in terms of philosophy, implementation, speed, and ease of use. Developers shouldn’t be discouraged from moving between the two. In fact, working with both should be encouraged, because it makes developers more versatile and expands the range of projects they can work on.
Josh Justice
Reviewer
Big Nerd Ranch
Josh Justice has worked as a developer since 2004 across backend, frontend, and native mobile platforms. Josh values creating maintainable systems via testing, refactoring, and evolutionary design, and mentoring others to do the same. He currently serves as the Web Platform Lead at Big Nerd Ranch.