TanStack Router Và Lazy Loading: Cặp Đôi 'Lười' Mà Chất!

TanStack Router Và Lazy Loading: Cặp Đôi 'Lười' Mà Chất!

Xem nhanh

Chào bạn, welcome đến Short Tech Series – nơi mình sẽ chia sẻ những chiêu thức siêu hữu ích, gói gọn trong vài phút đọc! Không dài dòng, không phức tạp, chỉ có những mẹo technical ngon lành mà bạn có thể nắm bắt nhanh như chớp và áp dụng ngay tức thì. Hôm nay, chúng ta sẽ bắt đầu với chủ đề gì? Kéo xuống xem nhé! 😎

Chắc hẳn bạn đã quá quen với Lazy loading bằng React.lazy() cổ điển đúng khum nào? Hôm nay mình sẽ đưa bạn vào thế giới Lazy loading siêu "lười mà chất" với TanStack Router.

Bài viết này sử dụng TanStack Router (v1.1x) với Vite

Lazy Loading là gì?

Lazy loading giống như mẹo "giữ hàng" trong coding, chỉ tải mã nguồn khi người dùng thực sự cần, tựa như chuyến đi siêu thị khôn ngoan. Nếu gom hết đồ dùng cho cả năm, bạn sẽ mất nhiều thời gian và chi phí để vận chuyển, cất giữ. Nhưng nếu chỉ sắm những gì cần ngay lúc này, bạn tiết kiệm được chi phí, dù đôi khi phải tốn chút công sức ghé siêu thị thêm lần nữa khi đồ dùng hết.

Lazy loading giúp app tải nhanh hơn bằng cách chỉ tải nội dung cần thiết khi người dùng tương tác, tiết kiệm tài nguyên và cải thiện trải nghiệm.

Lazy Loading với Tanstack Router

React.lazy() thì như bạn tự tay thái rau ("cắt" code) – được thôi, nhưng hơi mệt. Còn TanStack Router với file-based routing thì giống robot thái hộ: nhanh gọn, type-safe, hoàn toàn tự động.

File-based routing

  • Cơ chế tự động tạo các tuyến (route) dựa trên cấu trúc thư mục và tên file trong thư mục src/routes/.
  • Mỗi file như route.tsx đại diện cho một tuyến, với plugin như TanStackRouterVite quét và xây dựng route tree tương ứng

TanStack Router chia code thành "hàng nóng" (critical) và "hàng lười" (non-critical).

  • Critical: Là các tuyến (route) chứa mã cần thiết để hiển thị ngay lập tức khi ứng dụng được tải lần đầu hoặc khi người dùng truy cập một tuyến cụ thể.
  • Non-Critical: Là các tuyến chứa mã không cần thiết phải tải ngay lập tức. Thay vào đó, mã này được tải theo yêu cầu (on-demand) khi người dùng thực sự truy cập vào tuyến đó.

Chỉ cần đặt tên file có đuôi .lazy.tsx - non-critical, router tự hiểu: "À, cái này để sau load cũng được!" và code của file đó sẽ được tự động split ra.

Tin vui cho các bạn đang dùng Webpack, bạn cũng có thể tận dụng thế mạnh tự động lazy loading theo file-based routing của TanStack Router bằng TanStackRouterWebpack plugin.

Nhưng hiện tại bạn chỉ có thể bật hoặc tắt tính năng autoCodeSplitting cho tất cả các files trong project mà không phân biệt giữa file .tsx.lazy.tsx, không thể thực hiện tùy chỉnh splitting code chỉ cho những files mà bạn mong muốn.

Bắt tay vào việc nào!

Giả sử ta có ứng dụng 2 trang: Home page (/) và Settings page (/settings).

Script đầy đủ 1 phát ăn ngay cho bạn mình để ở dưới cuối bài nhé.

Route files

  1. File index.tsx (critical, load ngay):
Copy
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/")({
  component: HomeComponent,
});

function HomeComponent() {
  return <div>Home page</div>;
}
  1. File settings.lazy.tsx (lazy, load khi cần):
Copy
import { createLazyFileRoute } from "@tanstack/react-router";

export const Route = createLazyFileRoute("/settings")({
  component: SettingsComponent,
});

function SettingsComponent() {
  return <div>Settings page</div>;
}

Build Với Vite: Kết Quả Ra Sao?

dist/
  ├── assets/
  │   ├── index-[hash].js          // Code critical (home, router logic,...)
  │   ├── index-[hash].css         // CSS chung (nếu có)
  │   └── settings.lazy-[hash].js  // Code lazy của trang /settings
  ├── index.html
  • index-[hash].js: Chứa code chính, bao gồm trang / và logic router.
  • settings-[hash].js: Chỉ load khi người dùng vào /settings

Nhờ Vite, bundle được tối ưu, mỗi trang lazy thành một file riêng. Người dùng vào / không phải tải code của /settings – tiết kiệm băng thông, nhanh chóng.

Tổng kết

Lazy loading với TanStack Router kết hợp file-based routing giúp tối ưu hóa hiệu suất ứng dụng bằng cách chỉ tải mã nguồn khi cần, đặc biệt với các file .lazy.tsx. Sử dụng plugin TanStackRouterVite việc code splitting trở nên tự động và type-safe, mang lại trải nghiệm mượt mà cho người dùng. Đã đủ "Wow" với bạn chưa nào, hẹn gặp lại các bạn ở bài viết tiếp theo của mình nhé 😎

Tham khảo:

Script

#!/bin/bash

# Tên dự án
PROJECT_NAME="my-tanstack-router-app"

# Kiểm tra xem npm có được cài đặt không
if ! command -v npm &> /dev/null; then
    echo "npm không được cài đặt. Vui lòng cài đặt Node.js và npm trước."
    exit 1
fi

# Tạo dự án React với Vite
echo "Đang tạo dự án React với Vite..."
npm create vite@latest $PROJECT_NAME -- --template react-ts
cd $PROJECT_NAME || exit

# Cài đặt dependencies
echo "Đang cài đặt dependencies..."
npm install @tanstack/react-router @tanstack/router-plugin

# Cấu hình vite.config.ts
echo "Đang cấu hình vite.config.ts..."
cat > vite.config.ts << 'EOL'
import { defineConfig } from 'vite'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    TanStackRouterVite(),
    react(),
  ],
})
EOL

# Tạo cấu trúc thư mục routes
echo "Đang tạo cấu trúc thư mục routes..."
mkdir -p src/routes

# Tạo file __root.tsx
echo "Đang tạo file src/routes/__root.tsx..."
cat > src/routes/__root.tsx << 'EOL'
import { Outlet, createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute({
  component: RootComponent,
})

function RootComponent() {
  return (
    <div>
      <h1>Ứng dụng TanStack Router</h1>
      <Outlet />
    </div>
  )
}
EOL

# Tạo file index.tsx
echo "Đang tạo file src/routes/index.tsx..."
cat > src/routes/index.tsx << 'EOL'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: HomeComponent,
})

function HomeComponent() {
  return <div>Home page</div>
}
EOL

# Tạo file settings.tsx
echo "Đang tạo file src/routes/settings.tsx..."
cat > src/routes/settings.tsx << 'EOL'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/settings')({
  component: SettingsComponent,
})

function SettingsComponent() {
  return <div>Settings page</div>
}
EOL

# Cập nhật file App.tsx
echo "Đang cập nhật file src/App.tsx..."
cat > src/App.tsx << 'EOL'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

function App() {
  return <RouterProvider router={router} />
}

export default App
EOL

# Cập nhật file main.tsx
echo "Đang cập nhật file src/main.tsx..."
cat > src/main.tsx << 'EOL'
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
EOL

# Xóa các file không cần thiết (nếu có)
rm -f src/App.css src/assets/react.svg

# Cập nhật index.css (tùy chọn)
echo "Đang cập nhật file src/index.css..."
cat > src/index.css << 'EOL'
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
}
EOL

# Khởi động dự án để kiểm tra
echo "Cài đặt hoàn tất! Đang khởi động dự án..."
npm run dev

echo "Dự án đã được tạo và chạy tại http://localhost:5173"