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:

  1. Thời gian khởi tạo ban đầu: Thời gian để tải và khởi chạy framework
  2. Kích thước bundle: Dung lượng tối thiểu cần thiết
  3. Thời gian render: Tốc độ render UI ban đầu và re-render
  4. Hiệu suất cập nhật: Tốc độ xử lý các thay đổi state
  5. Sử dụng bộ nhớ: Lượng bộ nhớ tiêu thụ
  6. Hiệu suất với danh sách lớn: Khả năng xử lý danh sách có nhiều item
  7. 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 18Vue 3Ghi chú
Khởi tạo ban đầuTrung bìnhNhanhVue thường nhanh hơn 10-15%
Kích thước bundle~40KB~20KBVue nhỏ hơn đáng kể
Thêm 1000 hàngTrung bìnhNhanhVue nhanh hơn khoảng 20%
Cập nhật 10 hàngNhanhRất nhanhVue nhanh hơn nhưng chênh lệch nhỏ
Chọn hàngNhanhNhanhTương đương
Xóa hàngTrung bìnhNhanhVue nhanh hơn khoảng 15%
Swap hàngTrung bìnhNhanhVue nhanh hơn khoảng 25%
Sử dụng bộ nhớCaoTrung bìnhVue 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  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:

  1. Concurrent Mode: Cho phép ưu tiên các cập nhật quan trọng và làm việc song song
  2. Fiber Architecture: Cho phép ngắt và tiếp tục quá trình render
  3. Memoization: Hệ thống memo toàn diện (React.memo, useMemo, useCallback)
  4. Suspense: Xử lý loading state hiệu quả
  5. Server Components: Giảm JavaScript gửi đến client
  6. Streaming SSR: Cải thiện TTFB và FCP

Điểm yếu về hiệu suất:

  1. Bundle size lớn: React + ReactDOM nặng hơn Vue
  2. 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
  3. Hydration chậm: Quá trình hydration có thể chậm trong các ứng dụng lớn
  4. Cần tối ưu thủ công: Cần nhiều công sức để tối ưu (memo, code splitting, etc.)
  5. 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:

  1. Reactivity System: Theo dõi dependencies chính xác, ít re-render không cần thiết
  2. Compiler Optimizations: Tối ưu hóa ở thời điểm biên dịch (static hoisting, patch flags)
  3. Bundle size nhỏ: Core framework nhẹ hơn đáng kể
  4. Template-based: Cho phép nhiều tối ưu hóa tĩnh
  5. Tự động tối ưu: Ít cần tối ưu thủ công hơn
  6. Hydration hiệu quả: Quá trình hydration thường nhanh hơn

Điểm yếu về hiệu suất:

  1. Thiếu Concurrent Mode: Không có tính năng tương đương với Concurrent Mode của React
  2. 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
  3. Ecosystem nhỏ hơn: Ít thư viện tối ưu hiệu suất chuyên biệt
  4. Khó debug reactivity: Đôi khi khó debug các vấn đề liên quan đến reactivity
  5. Í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:

  1. Ứ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
  2. 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
  3. Cần Server Components: Nếu bạn muốn tận dụng React Server Components
  4. 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
  5. Ứ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:

  1. Ứng dụng nhỏ đến trung bình: Tận dụng bundle size nhỏ và khởi động nhanh
  2. Cập nhật nhỏ và thường xuyên: Reactivity system xử lý tốt các cập nhật nhỏ
  3. Thiết bị có cấu hình thấp: Tiêu thụ ít bộ nhớ hơn
  4. Cần ít tối ưu thủ công: Nhiều tối ưu hóa tự động
  5. 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ụngReactVueGhi chú
Ứng dụng nhỏTốtRất tốtVue có lợi thế về bundle size
Ứng dụng lớnRất tốtTốtReact có nhiều công cụ cho ứng dụng lớn
Cập nhật nhỏ, thường xuyênTốtRất tốtVue có lợi thế nhờ reactivity system
Cập nhật lớn, phức tạpRất tốtTốtReact có lợi thế nhờ Concurrent Mode
Thiết bị cấu hình thấpTốtRất tốtVue tiêu thụ ít tài nguyên hơn
SSRRất tốtRất tốtCả hai đều mạnh với Next.js và Nuxt.js
Danh sách dàiTốtTốtCần virtualization cho cả hai
Thời gian phát triển ngắnTốtRất tốtVue í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 ưuReactVueGhi chú
MemoizationReact.memo, useMemo, useCallbackv-once, v-memo, computedVue có nhiều tối ưu tự động hơn
Code splittingReact.lazy + SuspensedefineAsyncComponentCả hai đều hiệu quả
Virtualizationreact-window, react-virtualizedvue-virtual-scrollerHiệu suất tương đương
Tránh re-rendershouldComponentUpdate, PureComponentReactivity systemVue ít cần tối ưu thủ công
State managementContext API, ReduxVuex, PiniaPinia nhẹ hơn Redux
SSRNext.jsNuxt.jsHiệ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

  1. Server Components: Cho phép components chạy trên server, giảm JavaScript gửi đến client
  2. Streaming SSR: Cải thiện TTFB và FCP bằng cách stream HTML
  3. Automatic Batching: Gom nhóm các cập nhật state để giảm số lần re-render
  4. Transition API: Đánh dấu các cập nhật không khẩn cấp
  5. 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

  1. Vapor Mode: Chế độ mới trong Vue 3.5+ loại bỏ Virtual DOM cho hiệu suất tốt hơn
  2. Improved SSR Hydration: Cải thiện quá trình hydration
  3. Compiler Macros: Tối ưu hóa compiler mạnh mẽ hơn
  4. Reactivity Transform: Cú pháp đơn giản hóa cho reactivity
  5. 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

  1. Islands Architecture: Chia nhỏ ứng dụng thành các “islands” độc lập
  2. Partial Hydration: Chỉ hydrate các phần tương tác của trang
  3. Edge Rendering: Render tại edge servers để giảm độ trễ
  4. Resumability: Thay thế hydration bằng “resuming” execution
  5. 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  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é.

Tài liệu tham khảo