Giới thiệu
Việc so sánh hiệu suất giữa các framework không phải là một nhiệm vụ đơn giản. Hiệu suất phụ thuộc vào nhiều yếu tố như kích thước ứng dụng, độ phức tạp của UI, tần suất cập nhật, và cách triển khai cụ thể. Tuy nhiên, chúng ta có thể phân tích một số khía cạnh chính để có cái nhìn tổng quan về hiệu suất của React và Vue.
Trong bài viết này, chúng ta sẽ khám phá:
- Benchmark và phân tích hiệu suất
- Điểm mạnh và điểm yếu về hiệu suất của mỗi framework
- Các trường hợp sử dụng phù hợp
- Xu hướng tối ưu hiệu suất trong tương lai
1. Benchmark và phân tích hiệu suất
Để so sánh hiệu suất một cách khách quan, chúng ta cần xem xét các benchmark và phân tích thực tế.
Các tiêu chí đánh giá hiệu suất
Khi so sánh hiệu suất của React và Vue, chúng ta cần xem xét các tiêu chí sau:
- Thời gian khởi tạo ban đầu: Thời gian để tải và khởi chạy framework
- Kích thước bundle: Dung lượng tối thiểu cần thiết
- Thời gian render: Tốc độ render UI ban đầu và re-render
- Hiệu suất cập nhật: Tốc độ xử lý các thay đổi state
- Sử dụng bộ nhớ: Lượng bộ nhớ tiêu thụ
- Hiệu suất với danh sách lớn: Khả năng xử lý danh sách có nhiều item
- Hiệu suất với các tương tác phức tạp: Khả năng xử lý các tương tác người dùng phức tạp
Benchmark JS Framework
Một trong những benchmark phổ biến nhất là JS Framework Benchmark, cung cấp các số liệu chi tiết về hiệu suất của các framework JavaScript.
Dưới đây là một số kết quả đáng chú ý (lưu ý rằng các số liệu có thể thay đổi theo thời gian với các phiên bản mới):
Tiêu chí | React 18 | Vue 3 | Ghi chú |
---|---|---|---|
Khởi tạo ban đầu | Trung bình | Nhanh | Vue thường nhanh hơn 10-15% |
Kích thước bundle | ~40KB | ~20KB | Vue nhỏ hơn đáng kể |
Thêm 1000 hàng | Trung bình | Nhanh | Vue nhanh hơn khoảng 20% |
Cập nhật 10 hàng | Nhanh | Rất nhanh | Vue nhanh hơn nhưng chênh lệch nhỏ |
Chọn hàng | Nhanh | Nhanh | Tương đương |
Xóa hàng | Trung bình | Nhanh | Vue nhanh hơn khoảng 15% |
Swap hàng | Trung bình | Nhanh | Vue nhanh hơn khoảng 25% |
Sử dụng bộ nhớ | Cao | Trung bình | Vue tiêu thụ ít bộ nhớ hơn |
Phân tích hiệu suất thực tế
Ngoài benchmark, chúng ta cần xem xét hiệu suất trong các ứng dụng thực tế:
Thời gian tải trang (First Contentful Paint):
// Ứng dụng cỡ trung bình, đã tối ưu
React: ~1.2s
Vue: ~0.9s
Thời gian tương tác (Time to Interactive):
// Ứng dụng cỡ trung bình, đã tối ưu
React: ~2.5s
Vue: ~2.1s
Ví dụ đơn giản về Time to Interactive:
// React - Cách tiếp cận cơ bản
function App() {
// Tải toàn bộ bundle React + các thư viện (React DOM, etc.): ~120KB
// + Component code: ~30KB
// + Thư viện UI: ~80KB
// = 230KB JavaScript cần parse và thực thi trước khi interactive
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
// React - Cách tiếp cận tối ưu
function App() {
// Chỉ tải core React + các component thiết yếu: ~80KB
// Lazy load các component khác sau khi trang đã interactive
return (
<div>
<Header />
<React.Suspense fallback={<Spinner />}>
<MainContent /> {/* Lazy loaded */}
</React.Suspense>
<Footer />
</div>
);
}
<!-- Vue - Cách tiếp cận cơ bản -->
<template>
<div>
<Header />
<MainContent />
<Footer />
</div>
</template>
<script>
// Tải toàn bộ bundle Vue: ~60KB
// + Component code: ~30KB
// + Thư viện UI: ~80KB
// = 170KB JavaScript cần parse và thực thi trước khi interactive
</script>
<!-- Vue - Cách tiếp cận tối ưu -->
<template>
<div>
<Header />
<Suspense>
<template #default>
<MainContent />
<!-- Async component -->
</template>
<template #fallback>
<Spinner />
</template>
</Suspense>
<Footer />
</div>
</template>
<script>
// Chỉ tải core Vue + các component thiết yếu: ~40KB
// Async components tự động được tải sau khi trang đã interactive
</script>
Đo lường thời gian tải và thực thi JavaScript:
// Trước khi tối ưu
React: 230KB JS => 1.8s parse/compile + 0.7s execute = 2.5s TTI
Vue: 170KB JS => 1.5s parse/compile + 0.6s execute = 2.1s TTI
// Sau khi tối ưu (code splitting)
React: 80KB JS => 0.7s parse/compile + 0.4s execute = 1.1s TTI
Vue: 40KB JS => 0.4s parse/compile + 0.3s execute = 0.7s TTI
Ví dụ về Time to Interactive trong React vs Vue:
// React - Trước khi tối ưu
import React, { useState, useEffect } from "react";
import { Chart } from "heavy-chart-library"; // Thư viện nặng
import { formatData } from "./utils";
function Dashboard() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
// Tải toàn bộ dữ liệu khi component mount
useEffect(() => {
fetch("/api/dashboard-data")
.then((res) => res.json())
.then((result) => {
setData(formatData(result)); // Xử lý dữ liệu nặng
setIsLoading(false);
});
}, []);
// Render chờ đợi và nội dung
return (
<div className="dashboard">
<h1>Dashboard</h1>
{isLoading ? (
<div>Loading...</div>
) : (
<>
<div className="stats">
{data.stats.map((stat) => (
<div key={stat.id} className="stat-card">
<h3>{stat.label}</h3>
<p>{stat.value}</p>
</div>
))}
</div>
<Chart data={data.chartData} /> {/* Component nặng */}
</>
)}
</div>
);
}
// React - Sau khi tối ưu
import React, { useState, useEffect, lazy, Suspense } from "react";
import { formatData } from "./utils";
// Lazy load component nặng
const Chart = lazy(() => import("./Chart"));
function OptimizedDashboard() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
// Tải dữ liệu cơ bản trước
useEffect(() => {
// Tải dữ liệu cơ bản nhanh hơn
fetch("/api/dashboard-stats")
.then((res) => res.json())
.then((result) => {
setData({
stats: result.stats,
chartData: null,
});
setIsLoading(false);
// Tải dữ liệu biểu đồ sau
fetch("/api/dashboard-charts")
.then((res) => res.json())
.then((chartResult) => {
setData((prev) => ({
...prev,
chartData: formatData(chartResult),
}));
});
});
}, []);
return (
<div className="dashboard">
<h1>Dashboard</h1>
{isLoading ? (
<div>Loading...</div>
) : (
<>
<div className="stats">
{data.stats.map((stat) => (
<div key={stat.id} className="stat-card">
<h3>{stat.label}</h3>
<p>{stat.value}</p>
</div>
))}
</div>
{data.chartData ? (
<Suspense fallback={<div>Loading chart...</div>}>
<Chart data={data.chartData} />
</Suspense>
) : (
<div>Loading chart data...</div>
)}
</>
)}
</div>
);
}
<!-- Vue - Trước khi tối ưu -->
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<div v-if="isLoading">Loading...</div>
<template v-else>
<div class="stats">
<div v-for="stat in data.stats" :key="stat.id" class="stat-card">
<h3>{{ stat.label }}</h3>
<p>{{ stat.value }}</p>
</div>
</div>
<Chart :data="data.chartData" />
<!-- Component nặng -->
</template>
</div>
</template>
<script>
import Chart from "heavy-chart-library-vue";
import { formatData } from "./utils";
export default {
components: {
Chart,
},
data() {
return {
data: null,
isLoading: true,
};
},
mounted() {
fetch("/api/dashboard-data")
.then((res) => res.json())
.then((result) => {
this.data = formatData(result); // Xử lý dữ liệu nặng
this.isLoading = false;
});
},
};
</script>
<!-- Vue - Sau khi tối ưu -->
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<div v-if="isLoading">Loading...</div>
<template v-else>
<div class="stats">
<div v-for="stat in data.stats" :key="stat.id" class="stat-card">
<h3>{{ stat.label }}</h3>
<p>{{ stat.value }}</p>
</div>
</div>
<!-- Sử dụng Suspense và Async Components -->
<Suspense v-if="data.chartData">
<template #default>
<Chart :data="data.chartData" />
</template>
<template #fallback>
<div>Loading chart...</div>
</template>
</Suspense>
<div v-else>Loading chart data...</div>
</template>
</div>
</template>
<script>
import { defineAsyncComponent } from "vue";
import { formatData } from "./utils";
// Lazy load component nặng
const Chart = defineAsyncComponent(() => import("./Chart.vue"));
export default {
components: {
Chart,
},
data() {
return {
data: {
stats: [],
chartData: null,
},
isLoading: true,
};
},
mounted() {
// Tải dữ liệu cơ bản trước
fetch("/api/dashboard-stats")
.then((res) => res.json())
.then((result) => {
this.data.stats = result.stats;
this.isLoading = false;
// Tải dữ liệu biểu đồ sau
fetch("/api/dashboard-charts")
.then((res) => res.json())
.then((chartResult) => {
this.data.chartData = formatData(chartResult);
});
});
},
};
</script>
Kết quả đo TTI trên Lighthouse:
// Trước khi tối ưu
React: 4.8s
Vue: 4.3s
// Sau khi tối ưu
React: 2.5s (giảm 48%)
Vue: 2.1s (giảm 51%)
Hiệu suất với danh sách lớn (10,000 items):
// Không sử dụng virtualization
React: Chậm (5-7s)
Vue: Chậm (4-6s)
// Với virtualization
React: Nhanh (0.5-0.8s)
Vue: Nhanh (0.4-0.7s)
Ví dụ cập nhật danh sách trong React:
function LargeList() {
const [items, setItems] = useState(generateItems(10000));
const [filter, setFilter] = useState("");
// Cách không tối ưu - tính toán lại mỗi lần render
const filteredItems = items.filter((item) =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
// Cách tối ưu - chỉ tính toán khi items hoặc filter thay đổi
const optimizedFilteredItems = useMemo(() => {
console.time("filter");
const result = items.filter((item) =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
console.timeEnd("filter"); // Không tối ưu: ~150-300ms, Có tối ưu: chỉ chạy khi dependencies thay đổi
return result;
}, [items, filter]);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter items..."
/>
<div>
{optimizedFilteredItems.slice(0, 100).map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
Ví dụ tương tự trong Vue:
<template>
<div>
<input v-model="filter" placeholder="Filter items..." />
<div>
<div v-for="item in filteredItems.slice(0, 100)" :key="item.id">
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: this.generateItems(10000),
filter: "",
};
},
computed: {
// Vue tự động memoize computed properties
// và chỉ tính toán lại khi dependencies thay đổi
filteredItems() {
console.time("filter");
const result = this.items.filter((item) =>
item.name.toLowerCase().includes(this.filter.toLowerCase())
);
console.timeEnd("filter"); // Thường chạy nhanh hơn React ~10-15%
return result;
},
},
methods: {
generateItems(count) {
// Tạo danh sách items
},
},
};
</script>
Phân tích chi tiết các yếu tố ảnh hưởng
Startup Performance:
Vue thường có thời gian khởi động nhanh hơn do kích thước nhỏ hơn và quá trình hydration hiệu quả hơn. React có thể mất nhiều thời gian hơn, đặc biệt là khi sử dụng các thư viện bổ sung như Redux.
Runtime Performance:
React và Vue đều có hiệu suất runtime tốt, nhưng Vue thường xử lý các cập nhật nhỏ và thường xuyên tốt hơn nhờ hệ thống reactivity chi tiết. React có thể xử lý các cập nhật lớn và ít thường xuyên tốt hơn nhờ Concurrent Mode.
Memory Usage:
Vue thường sử dụng ít bộ nhớ hơn React, đặc biệt là trong các ứng dụng lớn. Điều này có thể quan trọng trên các thiết bị di động hoặc máy tính có cấu hình thấp.
2. Điểm mạnh và điểm yếu về hiệu suất của mỗi framework
React
Điểm mạnh về hiệu suất:
- Concurrent Mode: Cho phép ưu tiên các cập nhật quan trọng và làm việc song song
- Fiber Architecture: Cho phép ngắt và tiếp tục quá trình render
- Memoization: Hệ thống memo toàn diện (React.memo, useMemo, useCallback)
- Suspense: Xử lý loading state hiệu quả
- Server Components: Giảm JavaScript gửi đến client
- Streaming SSR: Cải thiện TTFB và FCP
Điểm yếu về hiệu suất:
- Bundle size lớn: React + ReactDOM nặng hơn Vue
- Cập nhật không cần thiết: Dễ gặp phải re-render không cần thiết nếu không cẩn thận
- Hydration chậm: Quá trình hydration có thể chậm trong các ứng dụng lớn
- Cần tối ưu thủ công: Cần nhiều công sức để tối ưu (memo, code splitting, etc.)
- Khó tránh re-render: Cần hiểu rõ về cách React hoạt động để tránh re-render
Vue
Điểm mạnh về hiệu suất:
- Reactivity System: Theo dõi dependencies chính xác, ít re-render không cần thiết
- Compiler Optimizations: Tối ưu hóa ở thời điểm biên dịch (static hoisting, patch flags)
- Bundle size nhỏ: Core framework nhẹ hơn đáng kể
- Template-based: Cho phép nhiều tối ưu hóa tĩnh
- Tự động tối ưu: Ít cần tối ưu thủ công hơn
- Hydration hiệu quả: Quá trình hydration thường nhanh hơn
Điểm yếu về hiệu suất:
- Thiếu Concurrent Mode: Không có tính năng tương đương với Concurrent Mode của React
- Hiệu suất với cập nhật lớn: Có thể chậm hơn với các cập nhật lớn và phức tạp
- Ecosystem nhỏ hơn: Ít thư viện tối ưu hiệu suất chuyên biệt
- Khó debug reactivity: Đôi khi khó debug các vấn đề liên quan đến reactivity
- Ít tính năng thử nghiệm: Ít tính năng hiệu suất tiên tiến như Server Components
3. Các trường hợp sử dụng phù hợp
Dựa trên phân tích hiệu suất, chúng ta có thể xác định các trường hợp sử dụng phù hợp cho mỗi framework.
Khi nào nên chọn React
React có thể là lựa chọn tốt hơn về hiệu suất trong các trường hợp sau:
- Ứng dụng phức tạp với nhiều tương tác: React Concurrent Mode giúp ứng dụng phản hồi tốt hơn
- Cập nhật lớn và ít thường xuyên: React xử lý tốt các cập nhật lớn
- Cần Server Components: Nếu bạn muốn tận dụng React Server Components
- Ecosystem quan trọng: Nếu bạn cần nhiều thư viện tối ưu hiệu suất chuyên biệt
- Ứng dụng lớn với nhiều developer: Cấu trúc rõ ràng và quy tắc nghiêm ngặt
Ví dụ thực tế:
// React xử lý tốt các cập nhật phức tạp với Concurrent Mode
function ComplexDashboard() {
const [data, setData] = useState(initialData);
// Cập nhật lớn nhưng không chặn UI
const updateData = useCallback(() => {
// startTransition đánh dấu cập nhật không khẩn cấp
startTransition(() => {
setData(generateNewData(10000));
});
}, []);
return (
<div>
<button onClick={updateData}>Update All Data</button>
<DataGrid data={data} />
</div>
);
}
Khi nào nên chọn Vue
Vue có thể là lựa chọn tốt hơn về hiệu suất trong các trường hợp sau:
- Ứng dụng nhỏ đến trung bình: Tận dụng bundle size nhỏ và khởi động nhanh
- Cập nhật nhỏ và thường xuyên: Reactivity system xử lý tốt các cập nhật nhỏ
- Thiết bị có cấu hình thấp: Tiêu thụ ít bộ nhớ hơn
- Cần ít tối ưu thủ công: Nhiều tối ưu hóa tự động
- Thời gian phát triển quan trọng: Ít phải lo về tối ưu hiệu suất
Ví dụ thực tế:
<template>
<div>
<input v-model="searchTerm" placeholder="Search..." />
<!-- Vue tự động chỉ cập nhật các items cần thiết -->
<div v-for="item in filteredItems" :key="item.id">
{{ item.name }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
})),
searchTerm: "",
};
},
computed: {
// Reactivity system tự động theo dõi dependencies
filteredItems() {
return this.items.filter((item) =>
item.name.toLowerCase().includes(this.searchTerm.toLowerCase())
);
},
},
};
</script>
Bảng so sánh các trường hợp sử dụng
Trường hợp sử dụng | React | Vue | Ghi chú |
---|---|---|---|
Ứng dụng nhỏ | Tốt | Rất tốt | Vue có lợi thế về bundle size |
Ứng dụng lớn | Rất tốt | Tốt | React có nhiều công cụ cho ứng dụng lớn |
Cập nhật nhỏ, thường xuyên | Tốt | Rất tốt | Vue có lợi thế nhờ reactivity system |
Cập nhật lớn, phức tạp | Rất tốt | Tốt | React có lợi thế nhờ Concurrent Mode |
Thiết bị cấu hình thấp | Tốt | Rất tốt | Vue tiêu thụ ít tài nguyên hơn |
SSR | Rất tốt | Rất tốt | Cả hai đều mạnh với Next.js và Nuxt.js |
Danh sách dài | Tốt | Tốt | Cần virtualization cho cả hai |
Thời gian phát triển ngắn | Tốt | Rất tốt | Vue ít cần tối ưu thủ công |
4. Tối ưu hiệu suất trong các framework
Để đạt được hiệu suất tối đa, chúng ta cần áp dụng các kỹ thuật tối ưu phù hợp với mỗi framework.
Tối ưu hiệu suất React
// 1. Sử dụng React.memo để tránh re-render không cần thiết
const ExpensiveComponent = React.memo(({ data }) => {
// Render logic
});
// 2. Sử dụng useMemo cho các tính toán đắt đỏ
function SearchResults({ items, query }) {
const filteredItems = useMemo(() => {
console.log("Filtering items...");
return items.filter((item) =>
item.name.toLowerCase().includes(query.toLowerCase())
);
}, [items, query]);
return (
<ul>
{filteredItems.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
// 3. Sử dụng useCallback cho event handlers
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []);
return <ChildComponent onClick={handleClick} />;
}
// 4. Code splitting với React.lazy và Suspense
const LazyComponent = React.lazy(() => import("./LazyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
// 5. Virtualization cho danh sách dài
import { FixedSizeList } from "react-window";
function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>{items[index].name}</div>
);
return (
<FixedSizeList
height={500}
width={300}
itemCount={items.length}
itemSize={35}
>
{Row}
</FixedSizeList>
);
}
Tối ưu hiệu suất Vue
<!-- 1. Sử dụng v-once cho nội dung tĩnh -->
<template>
<div>
<header v-once>
<h1>{{ title }}</h1>
<logo />
</header>
<main>
<!-- Nội dung động -->
</main>
</div>
</template>
<!-- 2. Sử dụng v-memo để memoise một phần của template -->
<template>
<div>
<div v-for="item in items" :key="item.id">
<!-- Chỉ re-render khi item.id hoặc item.selected thay đổi -->
<div v-memo="[item.id, item.selected]">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</div>
</div>
</div>
</template>
<!-- 3. Sử dụng computed properties với tham số -->
<script>
export default {
data() {
return {
items: [],
// Cache cho computed property
itemCache: new Map(),
};
},
methods: {
// Tương tự như useMemo với dependencies
getFilteredItems(query) {
if (this.itemCache.has(query)) {
return this.itemCache.get(query);
}
const result = this.items.filter((item) => item.name.includes(query));
this.itemCache.set(query, result);
return result;
},
},
};
</script>
<!-- 4. Sử dụng shallowRef và shallowReactive trong Vue 3 -->
<script setup>
import { shallowRef, shallowReactive } from "vue";
// Chỉ theo dõi thay đổi ở cấp cao nhất
const state = shallowReactive({
user: { name: "John", settings: { theme: "dark" } },
});
// Tương tự, chỉ theo dõi .value, không theo dõi các thuộc tính bên trong
const data = shallowRef({ count: 0, items: [] });
</script>
<!-- 5. Virtualization với vue-virtual-scroller -->
<template>
<RecycleScroller
class="scroller"
:items="items"
:item-size="32"
key-field="id"
v-slot="{ item }"
>
<div class="user-item">
{{ item.name }}
</div>
</RecycleScroller>
</template>
So sánh hiệu quả của các kỹ thuật tối ưu
Kỹ thuật tối ưu | React | Vue | Ghi chú |
---|---|---|---|
Memoization | React.memo, useMemo, useCallback | v-once, v-memo, computed | Vue có nhiều tối ưu tự động hơn |
Code splitting | React.lazy + Suspense | defineAsyncComponent | Cả hai đều hiệu quả |
Virtualization | react-window, react-virtualized | vue-virtual-scroller | Hiệu suất tương đương |
Tránh re-render | shouldComponentUpdate, PureComponent | Reactivity system | Vue ít cần tối ưu thủ công |
State management | Context API, Redux | Vuex, Pinia | Pinia nhẹ hơn Redux |
SSR | Next.js | Nuxt.js | Hiệu suất tương đương |
5. Xu hướng tối ưu hiệu suất trong tương lai
Cả React và Vue đều đang phát triển các tính năng mới để cải thiện hiệu suất. Dưới đây là một số xu hướng đáng chú ý:
Tính năng mới trong React
- Server Components: Cho phép components chạy trên server, giảm JavaScript gửi đến client
- Streaming SSR: Cải thiện TTFB và FCP bằng cách stream HTML
- Automatic Batching: Gom nhóm các cập nhật state để giảm số lần re-render
- Transition API: Đánh dấu các cập nhật không khẩn cấp
- Suspense for Data Fetching: Xử lý loading state hiệu quả hơn
// Server Components
// ServerComponent.js - chạy trên server
export default function ServerComponent() {
// Có thể truy cập trực tiếp database, filesystem
// Không gửi JS đến client
const data = await db.query('SELECT * FROM products');
return (
<div>
<h2>Products (rendered on server)</h2>
<ul>
{data.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
</div>
);
}
// Automatic Batching trong React 18
function AutoBatchingExample() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
// React 18 tự động batch cả hai cập nhật state này
// chỉ gây ra một lần re-render thay vì hai
setCount(c => c + 1);
setFlag(f => !f);
// Ngay cả trong promises, setTimeout, và event handlers native
// React 18 vẫn batch các cập nhật
setTimeout(() => {
setCount(c => c + 1); // Không gây re-render ngay lập tức
setFlag(f => !f); // Không gây re-render ngay lập tức
// Chỉ re-render một lần sau khi cả hai cập nhật hoàn tất
}, 1000);
}
return (
<div>
<button onClick={handleClick}>Update State</button>
<div>Count: {count}</div>
<div>Flag: {flag.toString()}</div>
</div>
);
}
// Transition API
function SearchWithTransition() {
const [isPending, startTransition] = useTransition();
const [filterText, setFilterText] = useState("");
function handleChange(e) {
// Cập nhật input là khẩn cấp - hiển thị ngay
const value = e.target.value;
// Đánh dấu việc lọc danh sách là không khẩn cấp
// React sẽ ưu tiên các tương tác người dùng khác trước
startTransition(() => {
setFilterText(value);
});
}
return (
<div>
<input
onChange={handleChange}
placeholder="Search products..."
/>
{/* Hiển thị trạng thái đang xử lý */}
{isPending ? (
<div className="loading-indicator">Filtering results...</div>
) : null}
{/* Component nặng, có thể gây trễ UI */}
<ProductList filterText={filterText} />
</div>
);
}
Tính năng mới trong Vue
- Vapor Mode: Chế độ mới trong Vue 3.5+ loại bỏ Virtual DOM cho hiệu suất tốt hơn
- Improved SSR Hydration: Cải thiện quá trình hydration
- Compiler Macros: Tối ưu hóa compiler mạnh mẽ hơn
- Reactivity Transform: Cú pháp đơn giản hóa cho reactivity
- Suspense Improvements: Cải thiện Suspense API
<!-- Vapor Mode (coming soon) -->
<template>
<div>
<h2>Products List</h2>
<input v-model="searchQuery" placeholder="Search products..." />
<ul>
<li v-for="product in filteredProducts" :key="product.id">
{{ product.name }} - ${{ product.price }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
// Vapor mode sẽ biên dịch template thành DOM operations trực tiếp
// thay vì sử dụng Virtual DOM, tăng hiệu suất lên đáng kể
const products = ref([
{ id: 1, name: "Laptop", price: 999 },
{ id: 2, name: "Phone", price: 699 },
{ id: 3, name: "Tablet", price: 399 },
// ... more products
]);
const searchQuery = ref("");
// Vue tự động theo dõi dependencies và chỉ tính toán lại khi cần
const filteredProducts = computed(() => {
if (!searchQuery.value) return products.value;
const query = searchQuery.value.toLowerCase();
return products.value.filter((product) =>
product.name.toLowerCase().includes(query)
);
});
</script>
<!-- Compiler Macros và Improved Reactivity -->
<template>
<div>
<h3>Product Editor</h3>
<input v-model="product.name" placeholder="Product name" />
<input v-model.number="product.price" type="number" placeholder="Price" />
<button @click="saveProduct">Save</button>
</div>
</template>
<script setup>
import { defineModel, reactive } from "vue";
// Tối ưu hóa two-way binding với defineModel
// Đơn giản hóa việc truyền props và emit events
const model = defineModel();
// Reactive state - hiệu suất tốt hơn ref cho các đối tượng phức tạp
const product = reactive({
name: "",
price: 0,
inStock: true,
});
// Method để lưu sản phẩm
function saveProduct() {
// Vue 3 tự động batch các cập nhật reactivity
// tương tự như Automatic Batching trong React 18
product.lastUpdated = new Date();
product.savedCount = (product.savedCount || 0) + 1;
// Chỉ gây ra một lần re-render
console.log("Product saved:", product);
}
</script>
<!-- Suspense và Async Components -->
<template>
<Suspense>
<template #default>
<AsyncProductList />
</template>
<template #fallback>
<div class="loading">Loading products...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from "vue";
// Code-splitting và lazy-loading để giảm kích thước bundle ban đầu
const AsyncProductList = defineAsyncComponent(
() => import("./ProductList.vue")
);
</script>
Xu hướng chung
- Islands Architecture: Chia nhỏ ứng dụng thành các “islands” độc lập
- Partial Hydration: Chỉ hydrate các phần tương tác của trang
- Edge Rendering: Render tại edge servers để giảm độ trễ
- Resumability: Thay thế hydration bằng “resuming” execution
- Fine-grained Reactivity: Hệ thống reactivity chi tiết hơn
// Islands Architecture (ví dụ với Astro)
---
// Có thể import cả React và Vue components trong cùng một trang
import InteractiveCounter from '../components/InteractiveCounter.vue';
import ProductSearch from '../components/ProductSearch.jsx';
import StaticContent from '../components/StaticContent.astro';
---
<html>
<body>
<header>
<h1>Static content (zero JS)</h1>
<p>This part of the page doesn't ship any JavaScript to the client</p>
</header>
<main>
<StaticContent title="About Our Products" />
<!-- Chỉ hydrate khi component hiển thị trong viewport -->
<InteractiveCounter client:visible />
<!-- Chỉ hydrate sau khi trang đã load xong, ưu tiên LCP -->
<ProductSearch client:idle />
<!-- Load và hydrate ngay lập tức, dùng cho các component quan trọng -->
<ShoppingCart client:load />
</main>
<footer>More static content</footer>
</body>
</html>
Ví dụ về Partial Hydration với Qwik:
// Qwik tự động phân tích và chỉ gửi JS cần thiết cho tương tác
import { component$, useSignal } from "@builder.io/qwik";
export default component$(() => {
// JS cho counter này chỉ được tải khi người dùng click vào button
const count = useSignal(0);
return (
<div>
<h1>Resumable Counter</h1>
<p>Count: {count.value}</p>
{/* JS chỉ được tải khi người dùng hover vào button */}
<button onClick$={() => count.value++}>Increment</button>
</div>
);
});
Tổng kết so sánh
Cả React và Vue đều là các framework hiệu suất cao, nhưng có những điểm mạnh và điểm yếu khác nhau:
- React mạnh về xử lý các ứng dụng phức tạp, cập nhật lớn, và có nhiều tính năng tiên tiến như Concurrent Mode và Server Components.
- Vue mạnh về hiệu suất khởi động, kích thước nhỏ, và xử lý các cập nhật nhỏ và thường xuyên nhờ hệ thống reactivity chi tiết.
Lựa chọn giữa React và Vue nên dựa trên yêu cầu cụ thể của dự án, không chỉ về hiệu suất mà còn về ecosystem, team expertise, và các yếu tố khác.
Trong tương lai, cả hai framework đều đang phát triển các tính năng mới để cải thiện hiệu suất, với xu hướng chung là giảm JavaScript gửi đến client, tối ưu hóa hydration, và cải thiện trải nghiệm người dùng.
Anw, trong bài viết này cũng sự so sánh cũng khá là chủ quan trong quá trình làm việc và tiếp xúc giữa 2 framework trên và tham khảo nhiều nguồn nên có sai sót hay góp ý hãy để lại bình luận nhé.