特性の追加
特性(Feature)は自己完結型のビジネスロジックのスライスです。packages/features/<name> に独立したパッケージとして置かれ、View・Composable・Store・ルートモジュールをまとめて管理します。
1. フォルダ規約
packages/features/<name>/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # 公開エントリ:ルート + Store を再エクスポート
│ ├── routes.ts # RouteRecordRaw[]
│ ├── store.ts # Pinia Store(任意)
│ ├── composables/
│ ├── views/
│ ├── components/ # 特性プライベートコンポーネント
│ └── locales/
│ ├── ja.json
│ └── en-US.jsonパッケージ名は @vh5/feature-<name>。
2. 手順
ステップ 1 — スキャフォールド
bash
# 既存の特性パッケージをコピーしてから:
pnpm installpackage.json:
json
{
"name": "@vh5/feature-product",
"version": "0.0.0",
"private": true,
"main": "src/index.ts",
"dependencies": {
"@vh5/api": "workspace:*",
"@vh5/services": "workspace:*",
"@vh5/core-base": "workspace:*",
"vue": "catalog:",
"vue-router": "catalog:",
"pinia": "catalog:"
}
}ステップ 2 — API + Service の追加
ts
// packages/api/src/product.ts
export const productApi = {
list: (params) => request.get<...>('/product/list', { params }),
detail: (id) => request.get<...>('/product/detail', { params: { id } }),
}ステップ 3 — Composable
ts
// packages/features/product/src/composables/use-product-detail.ts
export function useProductDetail(id: MaybeRef<number>) {
/* … */
}ステップ 4 — View
View では @vh5/app-shell/ui のアダプターコンポーネント(<AppButton>・<AppCell> など)を使用し、nut-button / van-button / var-button を直接インポートしません。
vue
<script setup lang="ts">
const { data, loading, error } = useProductDetail(() => Number(route.query.id));
</script>
<template>
<AppLoading v-if="loading" />
<AppError v-else-if="error" :error="error" />
<article v-else-if="data">
<h1>{{ data.title }}</h1>
<p>¥{{ data.price }}</p>
</article>
</template>ステップ 5 — ルートモジュール
ts
export const productRoutes: RouteRecordRaw[] = [
{
path: "/product",
name: "product-list",
component: () => import("./views/List.vue"),
meta: { title: "商品一覧", authority: ["user", "admin"] },
},
];ステップ 6 — 公開エントリー
ts
// packages/features/product/src/index.ts
export { productRoutes } from "./routes";
export { useProductStore } from "./store";
export * from "./composables/use-product-detail";ステップ 7 — アプリシェルに登録
ts
// packages/app-shell/src/router/index.ts
import { productRoutes } from "@vh5/feature-product";
import { mergeRouteModules } from "@vh5/utils";
export const routes = mergeRouteModules([...authRoutes, ...productRoutes, ...userRoutes]);各アダプターアプリは共有シェルをマウント済みです。各アプリで個別に設定する必要はありません。
3. チェックリスト
- [ ] 他の特性パッケージ(
@vh5/feature-*)をインポートしていない - [ ]
fetch/axiosを直接呼び出していない——@vh5/services経由 - [ ] コンポーネントライブラリのコンポーネント(
nut-*・van-*・var-*)をインポートしていない - [ ] View でサーバーデータを取得する際は Composable を使用し Pinia を使わない
- [ ] ルートに
meta.titleとmeta.authorityが宣言されている - [ ] ロケール文字列は特性と同じ場所に置かれている
- [ ] Service / Composable のユニットテストが書かれている