iT邦幫忙

2024 iThome 鐵人賽

DAY 5
1
Modern Web

Vue 和 TypeScript 的最佳實踐:成為前端工程師的進階利器系列 第 5

Day 5: Vue Router 與 TypeScript:型別安全的路由管理

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20240916/201174617VPxfzoK1i.jpg

介紹

在構建單頁應用(SPA)時,路由管理是一個至關重要的部分。Vue Router 是 Vue.js 官方提供的路由解決方案,與 Vue 3 的 Composition API 緊密結合。在這篇文章中,我們將探討如何結合 Vue Router 和 TypeScript,實現型別安全的路由管理,確保在開發過程中享受 TypeScript 帶來的靜態型別檢查和自動補全功能。

為什麼要使用 Vue Router 和 TypeScript?

Vue Router 是 Vue 生態系統中的核心部分,負責應用內部不同視圖之間的導航。隨著應用的規模增長,路由配置可能會變得複雜且難以管理。將 TypeScript 引入路由管理可以帶來以下優勢:

  1. 靜態型別檢查:減少運行時錯誤,確保路由名稱、參數等的一致性。
  2. 自動補全:增強開發體驗,讓我們可以快速編寫和管理路由。
  3. 更加安全的導航操作:在導航時可以明確知道哪些參數是可用的,哪些是必填的。

步驟 1:安裝 Vue Router

首先,我們需要在 Vue 3 項目中安裝 Vue Router。

bun add vue-router

安裝完成後,我們需要在項目中配置 Vue Router。

步驟 2:設置路由

接下來,我們將創建一個簡單的路由配置文件,並使用 TypeScript 來強化路由管理。
(檔案: src/router/index.ts)

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

export enum RoutesStatus {
  Home = 'Home',
  About = 'About',
  User = 'User'
}

// 定義路由配置的型別
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: RoutesStatus.Home,
    component: () => import('../pages/Home.vue'),
  },
  {
    path: '/about',
    name: RoutesStatus.About,
    component: () => import('../pages/About.vue'),
  },
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL ?? ''),
  routes,
});

export default router;

在這個例子中,我們使用了 RouteRecordRaw 來明確定義路由的結構和型別,確保每個路由的 pathnamecomponent 都符合預期。

步驟 3:在 Vue 應用中使用 Vue Router

接下來,我們需要在應用的入口文件中將 Vue Router 集成到 Vue 應用中。
(src/main.ts)

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App);

app.use(router);
app.mount('#app');

這樣,我們的 Vue 應用就具備了路由功能,並且通過 TypeScript 強化了路由配置的型別檢查。

步驟 4:使用帶參數的動態路由

在實際應用中,很多路由需要動態參數。這裡我們來看一下如何在 Vue Router 中處理帶參數的路由,並結合 TypeScript 確保參數的正確性。 (src/router/indext.ts)

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: RoutesStatus.Home,
    component: () => import('../pages/Home.vue'),
  },
  {
    path: '/user/:id',
    name: RoutesStatus.User,
    component: () => import('../pages/User.vue'),
    props: (route) => ({ id: Number(route.params.id) }),
    meta: {
      isRequireAuth: true,
    },
  },
];

在這個範例中,我們定義了一個帶有 :id 參數的動態路由,並將路由參數轉換為 props 傳遞給組件。這種方式使我們能夠在組件中直接使用 props,同時 TypeScript 會自動推斷 id 的型別。

步驟 5:使用導航守衛

Vue Router 提供了全局和路由級別的導航守衛,用於在導航到某個路由之前進行驗證或執行某些邏輯操作。使用 TypeScript 可以讓導航守衛更加安全和明確。

router.beforeEach(to => {
  if (!to.meta.isRequireAuth) return true;
  // 這裡設定如果需要驗證則放行
  return false;
});

在這個例子中,router 的 metaData 如果不需要驗證則直接放行,否則不放行,中間的部分可以去判斷有無權限,有的話放行沒的話不放行。

我們這裡簡單做一個驗證是否通過的範例: (檔案:src/composables/useAuth.ts)

import * as zod from 'zod';

export const validateSchema = zod.object({
  isAuth: zod.boolean(),
});

export type ValidateSchema = zod.infer<typeof validateSchema>;

export const useAuth = () => {

  const checkIfAllowPass = async (): Promise<boolean> => {
    const token = localStorage.getItem('auth');
    if (token === null || token.trim() === '') return false;

    const response = await fetch('https://api.example.com/validateToken', {
      headers: {
        Authorization: `Bearer ${token}`
      }
    });

    const authResult = await response.json();
    const validator = await validateSchema.safeParseAsync(authResult);
    if (!validator.success) {
      console.error(validator.error.errors);
      return false;
    }
    return validator.data.isAuth;
  };

  return {
    checkIfAllowPass,
  };
};

export type UseAuth = typeof useAuth;

這時我們導航守衛,可以改成這樣處理 (檔案 : src/router/index.ts)

// 上方記得 import { useAuth } from '../composables/useAuth';
router.beforeEach(async (to) => {
  if (!to.meta.isRequireAuth) return true;
  const { checkIfAllowPass } = useAuth();
  const isOkToPass = await checkIfAllowPass();
  if (isOkToPass) {
    return;
  }
  return false;
});

步驟 6:使用命名路由導航

為了避免直接操作 URL 字符串,Vue Router 支持使用命名路由進行導航,這樣可以使導航更加安全和可靠。

// 使用命名路由進行導航
router.push({ name: RoutesStatus.User, params: { id: 123 } });

在這裡,TypeScript 可以自動檢查 params 是否符合 User 路由的要求,避免我們傳入錯誤的參數或缺少必要的參數。

我們用 Home 頁面作為主要範例 Home.vue (檔案: src/pages/Home.vue)

<script setup lang="ts">
  import { useRouter } from 'vue-router'
  import { RoutesStatus } from '../router';
  const router = useRouter();

  const goToUserPage = (): void => {
    router.push({ name: RoutesStatus.User, params: { id: 123 } });
  };
</script>

<template>
  <h1>This is Home page</h1>
  <button @click="goToUserPage">Go to User Page</button>
</template>

步驟 7:結合 Vue Router 與 TypeScript 的全局型別擴展

當我們需要在 Vue 應用中多次使用路由參數時,可以考慮擴展全局型別,使得我們能夠在整個應用中共享路由型別。

// src/types/vue-router.d.ts
import 'vue-router';

declare module 'vue-router' {
  interface RouteMeta {
    isRequireAuth?: boolean;
  }
}

這樣做的好處是,我們可以在全局範圍內擴展 RouteMeta 的型別定義,比如為每個路由添加 requiresAuth 屬性,方便在導航守衛中使用。

結論

通過結合 Vue Router 和 TypeScript,我們可以實現型別安全的路由管理,不僅提升了代碼的可讀性和可維護性,還減少了因路由錯誤帶來的潛在問題。本文介紹了如何安裝和配置 Vue Router、處理動態路由、導航守衛、命名路由導航等常見場景,並展示了 TypeScript 如何為我們的開發提供強大的型別檢查支持。

接下來的文章中,我們將探討如何進一步使用 Vue Router 和 TypeScript,在大型應用中實現複雜的導航邏輯與狀態管理。


上一篇
Day 4: Pinia 的基本用法:在 Vue 中管理應用狀態
下一篇
Day 6: 在 UnoCSS 中應用原子 CSS 規則進行靈活的樣式設計
系列文
Vue 和 TypeScript 的最佳實踐:成為前端工程師的進階利器30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言