Vue vs React: Which Programming and View Style Fits Your Project?
This article compares Vue and React across programming syntax, view templating, component architecture, routing, state management, lifecycle hooks, and side‑effect handling, highlighting the trade‑offs in syntax complexity, flexibility, and developer ergonomics to help you choose the right framework for your needs.
1. Programming & View Styles
1.1 Programming Style
Vue uses a richer syntax with many built‑in directives, making it easier for beginners; React adopts a minimal syntax that requires more explicit JavaScript handling.
Example directives:
Vue
<input v-model="username"/>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>React
<input value={username} onChange={e => setUsername(e.target.value)}/>
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>Vue provides many directives that in React must be implemented with raw JavaScript, so developers often say "using Vue you operate Vue, using React you operate JavaScript".
React requires more manual handling ("magic" is less), while Vue automates many tasks.
Event handling example:
Vue
<button @click="handleClick('hello')">Click</button>
const handleClick = (msg) => { console.log(msg) }React
<button onClick={() => handleClick('hello')}>Click</button>
const handleClick = (msg) => () => { console.log(msg) }Both approaches have pros and cons: Vue aligns more with Vue‑style code, while React stays closer to pure JavaScript.
1.2 View Style
Vue uses <template> string templates that are close to HTML, easy to learn but sometimes less flexible.
React uses JSX syntax, which feels like JavaScript, imposes stricter rules (camelCase props, self‑closing tags, etc.) but integrates well with JavaScript logic.
Conditional rendering example (different implementations):
Vue
<template>
<h1 v-if="level === 1">Title 1</h1>
<h2 v-if="level === 2">Title 2</h2>
</template>React
const App = () => {
const level = 1;
const Tag = 'h' + level;
return (
<div>
<Tag>Title {level}</Tag>
</div>
);
};When many conditions are needed, JSX offers more flexibility than static template strings.
2. Components, Routing & State Management
2.1 Component Style
Vue 2 uses the Options API (less flexible, this binding issues). Vue 3 introduces the Composition API, a function‑based approach with better reuse and no this problems.
React before version 16.8 relied on class components (heavy this usage). Since 16.8, function components with Hooks provide a composition model similar to Vue’s Composition API.
Component illustration (Vue):
<template>
<div class="my-component">
<!-- HTML template -->
</div>
</template>
<script>
export default {
// JavaScript code
}
</script>
<style>
.my-component { /* CSS */ }
</style>Component illustration (React):
import React from 'react';
import './MyComponent.css';
function MyComponent() {
// JavaScript code
return (
<div className="my-component">
{/* HTML template */}
</div>
);
}
export default MyComponent;Both frameworks are moving toward functional, hook‑based patterns, reducing reliance on this.
2.2 Routing Style
Vue uses vue-router (e.g., useRouter, useRoute), while React uses react-router with many separate hooks ( useLocation, useParams, useNavigate, etc.). Vue’s API is more concise.
Vue‑router example:
<!-- index.html -->
<div id="app"><router-view/></div>
// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
});
const app = createApp({});
app.use(router);
app.mount('#app');
// Home.vue
<template>
<div>
<h1>Home Page</h1>
<button @click="goToAbout">Go to About Page</button>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const goToAbout = () => router.push('/about');
</script>
// About.vue
<template>
<div>
<h1>About Page</h1>
<p>Param: {{ $route.params.id }}</p>
<router-link to="/">Go to Home Page</router-link>
</div>
</template>
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
</script>React‑router example:
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link, useHistory } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
const App = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</div>
</Router>
);
const Home = () => {
const history = useHistory();
const handleClick = () => history.push('/about');
return (
<div>
<h1>Home Page</h1>
<button onClick={handleClick}>Go to About Page</button>
</div>
);
};
const About = () => {
const { id } = useParams();
return (
<div>
<h1>About Page</h1>
<p>Param: {id}</p>
<Link to="/">Go to Home Page</Link>
</div>
);
};
export default App;2.3 State Management Style
Vue uses Vuex or Pinia, which are designed specifically for Vue and share similar APIs. React uses Redux or MobX, which are framework‑agnostic.
Key differences:
API syntax: Vuex/Pinia are tightly coupled to Vue’s reactivity; Redux/MobX work with plain JavaScript.
Data flow: Redux follows a strict action → reducer → store → view pipeline; Vuex/Pinia manage state directly in a store without reducers.
Async handling: Redux typically needs middleware (e.g., thunk, saga); Vuex/Pinia handle async via actions; MobX can use async/await directly.
Setup overhead: Redux/MobX require more configuration; Vuex/Pinia are lightweight and integrate smoothly with Vue.
Pinia example:
// store.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({ count: 0 }),
actions: {
increment() { this.count++; }
}
});
// App.vue
<template>
<div>
<h1>Count: {{ count }}</h1>
<button @click="incrementCount">Increment</button>
</div>
</template>
<script setup>
import { useCounterStore } from './store';
const counterStore = useCounterStore();
const count = counterStore.count;
const incrementCount = () => counterStore.increment();
</script>
// main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
app.use(createPinia());
app.mount('#app');Redux Toolkit example:
// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: { increment(state) { state.count++; } }
});
export const { increment } = counterSlice.actions;
export const store = configureStore({ reducer: { counter: counterSlice.reducer } });
// App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './store';
function App() {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
const incrementCount = () => dispatch(increment());
return (
<div>
<h1>Count: {count}</h1>
<button onClick={incrementCount}>Increment</button>
</div>
);
}
export default App;
// index.js
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);3. Basic Feature Comparisons
3.1 Template Comparison
Vue updates the view via directives and templates; React updates via native JavaScript and JSX templates, offering more flexibility for complex structures.
3.2 Styling Comparison
Vue supports class and style as strings, arrays, or objects. React’s style accepts only objects, while className is a string (often managed with the classnames library).
3.3 Event Comparison
Vue’s event system is rich and declarative (e.g., @click="handleClick(index)"). React requires higher‑order functions to pass parameters (e.g., onClick={() => handleClick(i)}).
3.4 Form Comparison
Vue provides two‑way binding via v-model. React uses controlled or uncontrolled components, requiring explicit state handling.
3.5 Component Communication
Both frameworks use props for parent‑to‑child data. Vue adds emits for child‑to‑parent events; React uses callback props for the same purpose.
3.6 Logic Reuse
Vue 2 uses mixins; Vue 3 and React function components share the use hook pattern. Class‑based React components can also use render props or higher‑order components.
3.7 Content Distribution
Vue uses slots; React uses props.children to achieve similar composition.
3.8 DOM Manipulation
Both Vue and React expose a ref attribute to obtain direct DOM references.
4. Reactivity, Lifecycle & Side Effects
4.1 Reactive Data
Vue’s reactivity is built on Proxy, offering fine‑grained tracking. React’s state updates via setState (or useState) trigger a re‑render of the component tree.
4.2 Lifecycle Hooks
Vue provides a rich set of lifecycle hooks (beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted). React’s class components expose constructor, componentDidMount, componentDidUpdate, componentWillUnmount, while function components rely on useEffect for similar behavior.
4.3 Side‑Effect Handling
Vue uses watchEffect which automatically tracks dependencies. React uses useEffect, requiring an explicit dependency array. Vue’s cleanup runs via a callback argument; React’s cleanup is returned from the effect function.
Author: Lao Wang
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
