Comparação: Vue Router vs React Router
Você quer saber quais são as principais diferenças entre Vue Router e React Router? Leia este artigo e veja um exemplo prático com cada biblioteca!
Vue.js e React.js
Eu trabalho com Vue.js desde 2017, mas já faz alguns meses que estou participando de um projeto com React.js e quero compartilhar mais detalhes sobre essa experiência.
O objetivo deste artigo é comparar as bibliotecas Vue Router com React Router. Importante destacar que ambas bibliotecas são muito boas e cada uma tem suas vantagens.
Por isso meu propósito não é dizer qual é "melhor" ou "pior". Meu propósito é apresentar o funcionamento de cada uma para que você decida qual delas se adapta melhor ao seu projeto.
Exemplo Prático
Eu vou criar um pequeno exemplo que tenha as seguintes rotas:
- Home: Página inicial com alguns links.
- Login: Tela de login (neste caso um login fake só para demonstração).
- AboutCompany e AboutMe: Aqui há duas rotas que compartilham um mesmo layout (nested routes).
- User: Tela que exibe um usuário de acordo com os parâmetros passados na URL.
- Forbidden: Tela que o usuário é redirecionado quando tenta acessar alguma rota restrita a usuários logados.
Neste exemplo, somente as rotas AboutCompany, AboutMe e User são protegidas, ou seja, só podem acessadas por usuários autenticados.
Agora eu vou mostrar como fica este exemplo com Vue Router e posteriormente como fica com React Router.
Exemplo com Vue Router
Antes de continuar, você precisa de um projeto com Vue.js e Vue Router configurados. Se você tem dúvidas sobre isso, clique aqui.
Arquivo "/src/main.js":
import Vue from 'vue';
import App from './App.vue';
import router from './router';
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount('#app');
Arquivo "/src/router/index.js":
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '@/views/Home.vue';
import Login from '@/views/Login.vue';
import User from '@/views/User.vue';
import About from '@/views/About.vue';
import AboutMe from '@/views/AboutMe.vue';
import AboutCompany from '@/views/AboutCompany.vue';
import Forbidden from '@/views/Forbidden.vue';
const routes = [
{ path: '/', name: 'Home', component: Home, meta: { auth: false } },
{ path: '/login', name: 'Login', component: Login, meta: { auth: false } },
{ path: '/forbidden', name: 'Forbidden', component: Forbidden, meta: { auth: false } },
{ path: '/users/:id', name: 'User', component: User, meta: { auth: true } },
{
path: '/about',
name: 'About',
component: About,
children: [
{
path: '/about/me',
name: 'AboutMe',
component: AboutMe,
meta: { auth: true },
},
{
path: '/about/company',
name: 'AboutCompany',
component: AboutCompany,
meta: { auth: true },
},
],
},
];
Vue.use(VueRouter);
const router = new VueRouter({ routes });
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
const logged = window.localStorage.getItem('logged') === 'true';
if (!logged) {
next('/forbidden');
return;
}
}
next();
});
export default router;
Arquivo "/src/App.vue":
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
Arquivo "/src/views/Home.vue":
<template>
<div>
<h2>Home</h2>
<div>Logged: {{ logged }}</div>
<br />
<div>
<router-link to="/login">Login</router-link>
</div>
<div>
<router-link to="/about/me">About Me</router-link>
</div>
<div>
<router-link to="/about/company">About Company</router-link>
</div>
<div>
<router-link to="/users/1?name=John">User #1</router-link>
</div>
<div>
<router-link to="/users/2?name=Paul">User #2</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
data: () => ({
logged: window.localStorage.getItem('logged') === 'true',
}),
};
</script>
Arquivo "/src/views/Login.vue":
<template>
<div>
<h2>Login</h2>
<div>
<button @click="fakeLogin" v-if="!logged">Fake login</button>
<button @click="fakeLogout" v-else>Fake logout</button>
</div>
<div>
<router-link to="/">Back to Home</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Login',
data: () => ({
logged: window.localStorage.getItem('logged') === 'true',
}),
methods: {
fakeLogin() {
this.logged = true;
window.localStorage.setItem('logged', true);
},
fakeLogout() {
this.logged = false;
window.localStorage.setItem('logged', false);
},
},
};
</script>
Arquivo "/src/views/About.vue" e que serve de base para rotas filhas (AboutCompany e AboutMe):
<template>
<div>
<h2>About</h2>
<router-view />
<div>
<router-link to="/">Back to Home</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'About',
};
</script>
Arquivo "/src/views/AboutCompany.vue":
<template>
<div>
<h3>AboutCompany</h3>
</div>
</template>
<script>
export default {
name: 'AboutCompany',
};
</script>
Arquivo "/src/views/AboutMe.vue":
<template>
<div>
<h3>AboutMe</h3>
</div>
</template>
<script>
export default {
name: 'AboutMe',
};
</script>
Arquivo "/src/views/User.vue" que exibe os dados de usuário conforme parâmetros passados na URL (params e query):
<template>
<div>
<h2>Users</h2>
<div>id: {{ $route.params.id }}</div>
<div>name: {{ $route.query.name }}</div>
<div>
<router-link to="/">Back to Home</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Users',
};
</script>
Arquivo "/src/views/Forbidden.vue":
<template>
<div>
<h2>Forbidden</h2>
<div>Please sign in to access this page</div>
<div>
<router-link to="/login">Login</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Forbidden',
};
</script>
Exemplo com React Router
Agora vou mostrar o mesmo exemplo com React Router. Antes de continuar, você precisa de um projeto com React.js e React Router configurados. Se você tem dúvidas sobre isso, clique aqui para entender como instalar o React.js e clique aqui para entender como instalar o React Router.
Arquivo "/src/index.js":
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Arquivo "/src/App.js":
import React from 'react';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
import Home from './Home.js';
import Login from './Login.js';
import About from './About.js';
import User from './User.js';
import Forbidden from './Forbidden.js';
const PrivateRoute = ({ component: Component, ...rest }) => {
const render = (props) => {
if (window.localStorage.getItem('logged') === 'true') {
return <Component {...props} />;
}
return <Redirect to="/forbidden" />;
};
return <Route {...rest} render={render} />;
};
export default function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/forbidden" component={Forbidden} />
<PrivateRoute exact path="/users/:id" component={User} />
<PrivateRoute strict path="/about" component={About} />
</Switch>
</BrowserRouter>
);
}
Arquivo "/src/Home.js":
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
export default function Home(props) {
const [logged] = useState(window.localStorage.getItem('logged') === 'true');
return (
<div>
<h2>Home</h2>
<div>Logged: {JSON.stringify(logged)}</div>
<br />
<div>
<Link to="/login">Login</Link>
</div>
<div>
<Link to="/about/me">About Me</Link>
</div>
<div>
<Link to="/about/company">About Company</Link>
</div>
<div>
<Link to="/users/1?name=John">User #1</Link>
</div>
<div>
<Link to="/users/2?name=Paul">User #2</Link>
</div>
</div>
);
}
Arquivo "/src/Login.js":
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
export default function Login() {
const [logged, setLogged] = useState(window.localStorage.getItem('logged') === 'true');
const fakeLogin = () => {
setLogged(true);
window.localStorage.setItem('logged', true);
};
const fakeLogout = () => {
setLogged(false);
window.localStorage.setItem('logged', false);
};
const action = () => {
if (logged) {
return <button onClick={fakeLogout}>Fake logout</button>;
} else {
return <button onClick={fakeLogin}>Fake login</button>;
}
};
return (
<div>
<h2>Login</h2>
<div>{action()}</div>
<div>
<Link to="/">Back to Home</Link>
</div>
</div>
);
}
Arquivo "/src/About.js" e que serve de base para rotas filhas (AboutCompany e AboutMe):
import React from 'react';
import { Switch, Route, Link } from 'react-router-dom';
import AboutMe from './AboutMe.js';
import AboutCompany from './AboutCompany.js';
export default function About() {
return (
<div>
<h2>About</h2>
<Switch>
<Route exact path="/about/me" component={AboutMe}></Route>
<Route exact path="/about/company" component={AboutCompany}></Route>
</Switch>
<div>
<Link to="/">Back to Home</Link>
</div>
</div>
);
}
Arquivo "/src/AboutCompany.js":
import React from 'react';
export default function AboutCompany() {
return (
<div>
<h3>AboutCompany</h3>
</div>
);
}
Arquivo "/src/AboutMe.js":
import React from 'react';
export default function AboutMe() {
return (
<div>
<h3>AboutMe</h3>
</div>
);
}
Arquivo "/src/User.js":
import React from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
export default function User() {
const params = useParams();
const location = useLocation();
const query = new URLSearchParams(location.search);
return (
<div>
<h2>Users</h2>
<div>id: {params.id}</div>
<div>name: {query.get('name')}</div>
<div>
<Link to="/">Back to Home</Link>
</div>
</div>
);
}
Arquivo "/src/Forbidden.js":
import React from 'react';
import { Link } from 'react-router-dom';
export default function Forbidden() {
return (
<div>
<div>
<h2>Forbidden</h2>
<div>Please sign in to access this page</div>
<div>
<Link to="/login">Login</Link>
</div>
</div>
</div>
);
}
Diferenças entre Vue Router e React Router
Embora sejam bibliotecas muito parecidas, mas há algumas características diferentes.
Na minha opinião a principal diferença entre elas é na configuração das rotas. No Vue Router a configuração é feita por um arquivo JavaScript com um objeto (arquivo "/src/router/index.js") .
Por outro lado no React Router as rotas são configuradas diretamente na declaração do componente "Route". Mas é importante destacar que é possível criar um arquivo de configuração e gerar esse componente dinamicamente.
Ainda sobre essa questão a configuração das rotas, veja que no Vue Router o redirecionamento, caso um usuário não esteja autenticado, é feito por meio do evento beforeEach.
Já no React Router esse redirecionamento de usuários não autenticados é feito por meio da função render do componente Route.
Outra diferença é que no Vue Router você consegue acessar os valores da URL por meio dos variáveis $route.params e $route.query.
Já no React Router você consegue usar a função useParams para os parâmetros, mas não há uma implementação para acessar a query. Por isso precisamos usar a classe URLSearchParams do navegador.
Na minha opinião, estas são as diferenças mais importantes. As demais diferenças estão relacionadas a sintaxe e detalhes de cada framework.
Por fim
Só para destacar que neste artigo eu utilizei as seguintes versões de cada biblioteca:
Dúvidas ou sugestões é só entrar em contato. Abraço.