在Vue3中如何為路由Query引數標註型別

濮水大叔發表於2024-08-05

前言

最近釋出了一款支援IOC容器的Vue3框架:Zova。與以往的OOP或者Class方案不同,Zova在介面互動層面仍然採用Setup語法,僅僅在業務層面引入IOC容器。IOC容器猶如一把鑰匙,為我們開啟了業務工程化的大門,允許我們探索更多工程化方面的設計和能力。有網友提出一個非常好的建議:可否提供一些業務場景,說明有哪些是Class可做而Composable做不了的,這樣才更有說服力。

首先說明一點,其實沒有哪些業務需求是這個能做而那個不能做的。不同的程式設計正規化帶來的是不同的程式碼風格,不同的程式設計體驗,從不同的路徑指向開發效率和程式碼可維護性方面的評估。因此,最終根據使用者自身的偏好和業務實際需求而定。

那麼,在這裡,我們就針對這個話題如何為路由Query引數標註型別為例,看看Composable和IOC容器的程式碼風格究竟有什麼不同。

需求說明

這裡有一個頁面元件User,可以透過Query傳遞三個引數:

引數名 型別 預設值
id number 0
name string ''
married boolean false

Composable:原生

1. 訪問頁面

const router = useRouter();
router.push({
  path: '/test/demo/user',
  query: {
    id: 1,
    name: 'kevin',
    married: false.toString(),
  },
});

從Typescript型別的角度來看,這段程式碼有以下兩個問題:

  1. path:沒有型別約束和智慧提示。這會存在以下三個隱患:
    1. 記不住:如果路徑較長,或者單詞較複雜,就記不住路徑,需要從原始檔查詢
    2. 寫錯了:如果不小心寫錯了,沒有提示,只有到實際執行時才會暴露錯誤
    3. 被改了:如果後續維護程式碼時,路徑有了變更,那麼這裡的程式碼同樣沒有提示,只有到實際執行時才會暴露錯誤
  2. query:只有有限的型別約束,與業務型別並不一致
    1. 比如不支援Boolean型別,必須強制轉換為String型別

2. 獲取引數

const route = useRoute();
const id = parseInt(route.query.id ?? 0);
const name = route.query.name ?? '';
const married = route.query.married === 'true' ? true : false;

由於沒有提供型別工具,需要針對每一個引數單獨處理

Composable:useRouteQuery

1. 訪問頁面

(同上)

2. 獲取引數

import { useRouteQuery } from '@vueuse/router';

const id = useRouteQuery('id', 0, { transform: Number });
const name = useRouteQuery('name', '');
const married = useRouteQuery('married', 'false', {
  transform: value => {
    return value === 'true' ? true : false;
  },
});

IOC容器

1. 定義型別

import { zz } from 'zova';

export const QuerySchema = zz.object({
+  id: zz.number().default(0),
+  name: zz.string().default(''),
+  married: zz.boolean().default(false),
});
  • zz是在zod基礎上做的加強版,特別針對路由引數做了處理,支援array陣列和json物件,具體參見:Zova: zod
  • 在定義型別的同時可以指定預設值

2. 訪問頁面

const url = this.$router.resolvePath('/test/demo/user', {
  id: 0,
  name: 'kevin',
  married: false,
});
this.$router.push(url);
  • resolvePath的引數都有型別約束和智慧提示,並且與業務型別保持一致

3. 獲取引數

const id = this.$query.id;
const name = this.$query.name;
const married = this.$query.married;
  • 直接透過this.$query獲取引數值,有明確的型別,並且不需要處理預設值

總結

從上面的示例對比可以看出,採用IOC容器,可以實現定義使用的分離,而且定義側可以透過工具來建立腳手架,進一步簡化定義的書寫。由於TS型別和預設值等規範性程式碼都在定義側完成了,那麼在使用側程式碼就更加簡潔直觀了。不知您的程式碼風格偏好是什麼,是否還有更好的表達方式,歡迎在評論區交流。

參考資料

  • VueUse: useRouteQuery
  • Zova: 路由Query

相關文章