本文へ移動
cccskills
無料GitHub で公開

vue-routing

Vue Router v4 (Vue 3 default) — route configuration, nested routes, dynamic segments, navigation guards, lazy loading, typed routes, route meta, programmatic navigation. Vue Router v3 (Vue 2) pointer only. Use this skill to: - Configure routes with createRouter + createWebHistory. - Use useRoute / useRouter composables in components. - Lazy-load route components for code splitting. - Implement auth guards via meta + beforeEach. - Type route params for safer access. Do NOT use this skill for: - General Vue conventions (see vue-conventions). - State management (see vue-state-management). - Forms (see vue-forms). - Testing routes (see vue-testing).

インストール方法を見る

含まれるファイル(1)

  • SKILL.md11.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Vue Router v4 Patterns

The de-facto router for Vue 3. Use whatever the project has installed; don't introduce a new router lib.

Setup

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', name: 'home', component: HomeView },
    { path: '/about', name: 'about', component: () => import('@/views/AboutView.vue') }, // lazy
    { path: '/users', name: 'users', component: () => import('@/views/UsersView.vue') },
    { path: '/users/:id', name: 'user-detail', component: () => import('@/views/UserDetailView.vue'), props: true },
    { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFoundView.vue') },
  ],
});

export default router;
// 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');
<!-- App.vue -->
<template>
  <header>
    <RouterLink to="/">Home</RouterLink>
    <RouterLink to="/about">About</RouterLink>
  </header>
  <main>
    <RouterView />
  </main>
</template>

History modes

  • createWebHistory() — HTML5 History API (/about, /users/123). Requires server config to fall back to index.html for unmatched routes.
  • createWebHashHistory() — hash-based (/#/about). No server config needed; less SEO-friendly.
  • createMemoryHistory() — for SSR/testing.

Default to createWebHistory() unless deploying to static host without fallback.

Route definitions

const routes = [
  // Static
  { path: '/', name: 'home', component: HomeView },

  // Dynamic segment
  { path: '/users/:id', name: 'user', component: UserView, props: true },

  // Multiple dynamic segments
  { path: '/posts/:category/:slug', component: PostView },

  // Optional segment (Vue Router v4: use route alias or two routes)
  { path: '/users/:id/:tab?', component: UserView },           // ? means optional in v4

  // Catch-all
  { path: '/:pathMatch(.*)*', component: NotFoundView },

  // Aliases (multiple URLs → one component)
  { path: '/home', component: HomeView, alias: '/' },

  // Redirects
  { path: '/old-path', redirect: '/new-path' },
  { path: '/old-user/:id', redirect: (to) => ({ path: '/users/' + to.params.id }) },
];

Nested routes

{
  path: '/dashboard',
  component: DashboardLayout,
  children: [
    { path: '', component: DashboardHome },          // /dashboard
    { path: 'analytics', component: Analytics },      // /dashboard/analytics
    { path: 'settings', component: Settings },        // /dashboard/settings
  ],
}

DashboardLayout.vue includes <RouterView /> to render children:

<template>
  <nav>
    <RouterLink to="/dashboard">Home</RouterLink>
    <RouterLink to="/dashboard/analytics">Analytics</RouterLink>
  </nav>
  <RouterView />
</template>

Navigation hooks

Composables

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { computed } from 'vue';

const route = useRoute();
const router = useRouter();

// Reactive route info
const userId = computed(() => route.params.id as string);
const filter = computed(() => (route.query.filter as string) ?? 'all');

// Programmatic
function goToProfile() {
  router.push({ name: 'user', params: { id: '123' } });
}

function replaceWithFilter(value: string) {
  router.replace({ query: { ...route.query, filter: value } });
}
</script>

router.push, router.replace, router.back(), router.forward(), router.go(n).

Push variants

router.push('/users/123');                                       // string path
router.push({ path: '/users/123' });                             // object with path
router.push({ name: 'user', params: { id: '123' } });            // named route
router.push({ name: 'user', params: { id: '123' }, query: { tab: 'overview' } });
router.push({ path: '/users/123', hash: '#bio' });

Named routes are safer — refactoring the URL doesn't break the call.

<RouterLink>

<!-- Basic -->
<RouterLink to="/users">Users</RouterLink>

<!-- Named route -->
<RouterLink :to="{ name: 'user', params: { id: '123' } }">Alice</RouterLink>

<!-- Active class customization -->
<RouterLink to="/users" active-class="my-active" exact-active-class="my-exact-active">Users</RouterLink>

<!-- Custom rendering via slot -->
<RouterLink to="/users" custom v-slot="{ navigate, isActive }">
  <button :class="{ active: isActive }" @click="navigate">Users</button>
</RouterLink>

<!-- Replace instead of push -->
<RouterLink :to="{ name: 'login' }" replace>Log in</RouterLink>

For external links: plain <a href="https://...">. <RouterLink> is for internal navigation only.

Lazy loading

{
  path: '/users',
  name: 'users',
  component: () => import('@/views/UsersView.vue'),     // dynamic import → code split
}

Vite/Webpack handle code splitting automatically. Each lazy-loaded route becomes a separate chunk.

For grouped chunks:

component: () => import(/* webpackChunkName: "users" */ '@/views/UsersView.vue')

Navigation guards

Global

router.beforeEach(async (to, from) => {
  // returning false → cancel navigation
  // returning a route object → redirect
  // returning undefined or true → continue
  if (to.meta.requiresAuth && !await isAuthenticated()) {
    return { name: 'login', query: { redirect: to.fullPath } };
  }
});

router.afterEach((to, from, failure) => {
  // analytics, scroll restoration, etc.
});

Per-route

{
  path: '/admin',
  component: AdminView,
  beforeEnter: (to) => {
    if (!isAdmin()) return { name: 'home' };
  },
}

Per-component

<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router';

onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    return window.confirm('Unsaved changes — leave anyway?');
  }
});

onBeforeRouteUpdate((to, from) => {
  // Same component, route changed (e.g., /users/1 → /users/2)
  // Refetch data
});
</script>

Auth guard pattern

const routes = [
  { path: '/login', name: 'login', component: LoginView, meta: { requiresAuth: false } },
  { path: '/dashboard', name: 'dashboard', component: DashboardView, meta: { requiresAuth: true } },
  { path: '/admin', name: 'admin', component: AdminView, meta: { requiresAuth: true, requiresAdmin: true } },
];

router.beforeEach(async (to) => {
  const userStore = useUserStore();
  if (to.meta.requiresAuth && !userStore.isAuthenticated) {
    return { name: 'login', query: { redirect: to.fullPath } };
  }
  if (to.meta.requiresAdmin && !userStore.isAdmin) {
    return { name: 'dashboard' };
  }
});

Typed routes

Vue Router v4 doesn't auto-type params. Two approaches:

Manual TypeScript typing

// src/router/types.ts
declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean;
    requiresAdmin?: boolean;
    title?: string;
  }
}

// Per-route typing in components
const route = useRoute();
const userId = route.params.id as string;       // cast — type-safe-ish

unplugin-vue-router (file-based, generates types)

pnpm add -D unplugin-vue-router

vite.config.ts:

import VueRouter from 'unplugin-vue-router/vite';

export default defineConfig({
  plugins: [VueRouter({ routesFolder: 'src/pages' }), vue()],
});

Place files in src/pages/:

pages/
├── index.vue                    # /
├── about.vue                    # /about
├── users/
│   ├── index.vue                # /users
│   └── [id].vue                 # /users/:id
└── [...path].vue                # catch-all

Generated types in typed-router.d.ts provide compile-time safety:

const route = useRoute('/users/[id]');
const userId = route.params.id;                  // typed as string

Route meta for cross-cutting concerns

{
  path: '/dashboard',
  component: DashboardView,
  meta: {
    requiresAuth: true,
    title: 'Dashboard',
    layout: 'app',
  },
}

Read in App.vue for dynamic page titles:

<script setup lang="ts">
import { watch } from 'vue';
import { useRoute } from 'vue-router';

const route = useRoute();
watch(() => route.meta.title, (title) => {
  document.title = title ? `${title} | MyApp` : 'MyApp';
}, { immediate: true });
</script>

Scroll behavior

const router = createRouter({
  history: createWebHistory(),
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) return savedPosition;       // back/forward — restore position
    if (to.hash) return { el: to.hash, behavior: 'smooth' };
    return { top: 0 };                              // new route — scroll to top
  },
});

Vue Router v3 (Vue 2 legacy — pointer)

import VueRouter from 'vue-router';

const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '/', component: Home },
    { path: '/users/:id', component: UserView },
  ],
});

router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isAuth()) next('/login');
  else next();
});

Differences:

  • new VueRouter() instead of createRouter.
  • mode: 'history' | 'hash' instead of explicit history factory.
  • Guards take next callback instead of returning a route.
  • useRoute / useRouter composables don't exist; use this.$route / this.$router.

Migrating v3 → v4 is invasive (entire route file rewrites). Don't migrate as part of feature work.

Anti-patterns

  • ❌ Forgetting <RouterView /> in App.vue or layouts — routes don't render.
  • ❌ <a href="/internal"> for internal links — full page reload, defeats SPA.
  • ❌ Storing route state in component when query params suffice — duplicate truth.
  • ❌ Heavy logic in beforeEach that runs on every nav — cache where possible.
  • ❌ Mixing string paths and named routes inconsistently — pick one.
  • ❌ Forgetting to handle navigation failures (e.g., guard returns false) — async errors silently dropped.
  • ❌ Accessing route.params.id without type cast or schema validation — bare any-like access.
  • ❌ Redirects in beforeEach without checking to.name === 'login' — infinite loop on guard.
  • ❌ Long routes file — split by feature into multiple route arrays, then concat.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Angular 18-21 project structure, standalone components vs NgModule, control flow (@if/@for/@switch + *ngIf/*ngFor legacy), decorators, dependency injection (inject() function), lifecycle hooks, pipes, Angular Universal SSR pointer. Use this skill to: - Detect project style (standalone vs NgModule) and apply matching patterns. - Pick correct decorators and DI approach. - Use modern control flow (@if/@for/@switch) in Angular 17+ projects. - Apply `inject()` function over constructor injection where appropriate. - Wire bootstrap correctly (bootstrapApplication for standalone, AppModule for legacy). Do NOT use this skill for: - State management (see angular-state-and-rx). - Routing (see angular-routing). - Forms (see angular-forms). - Testing (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular forms: Reactive Forms (preferred — typed FormGroup/FormControl since Angular 14, FormBuilder, custom + async validators, FormArray, multi-step) and Template-driven (`[(ngModel)]` + FormsModule). Validation strategies, server error mapping, accessibility. Use this skill to: - Build Reactive Forms with typed FormGroup/FormControl. - Use FormBuilder для concise syntax. - Implement custom synchronous and async validators. - Wire FormArray for dynamic field lists. - Map server errors back to form fields. - Pick Reactive vs Template-driven (prefer Reactive). Do NOT use this skill for: - General conventions (see angular-conventions). - State management beyond forms (see angular-state-and-rx). - Routing (see angular-routing). - Testing forms (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular Router (built-in `@angular/router`) — route configuration for standalone and NgModule projects, functional guards (Angular 14.1+), lazy loading, route resolvers, typed params via signals/observables, programmatic navigation, route data and meta. Use this skill to: - Configure routes (standalone-style or NgModule-style). - Use functional guards (canActivate as function, preferred over class-based in 17+). - Lazy-load components or feature modules. - Implement auth guards via route meta + functional guards. - Read params/queries via `inject(ActivatedRoute)` + signals or RxJS. Do NOT use this skill for: - General conventions (see angular-conventions). - State management (see angular-state-and-rx). - Forms (see angular-forms). - Testing routes (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

State management for Angular 18-21: signals (signal/computed/effect), services-as-state, NgRx Store + Effects + Selectors, NgRx Component Store, NgRx Signals (newer signal-based store). RxJS essentials — operators, async pipe, takeUntilDestroyed, signal/observable interop. Use this skill to: - Pick the right state tool (signals / services / NgRx variant / vue-query equivalent). - Use signals correctly (signal/computed/effect — when each). - Build a Pinia-style service-as-state singleton. - Set up NgRx Store + Effects + Selectors. - Use RxJS without leaking subscriptions (async pipe, takeUntilDestroyed, Subject patterns). - Bridge signals ↔ observables via toSignal / toObservable. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing state (see angular-routing). - Form state (see angular-forms). - Testing state (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Testing Angular 18-21: TestBed, component harnesses (@angular/cdk/testing), Karma+Jasmine (default historical) vs Jest (jest-preset-angular, modern), Angular Testing Library (RTL-style). HttpClient mocking via HttpTestingController. NgRx Effects testing. Cypress / Playwright e2e. Use this skill to: - Detect runner (Karma+Jasmine vs Jest) and configure correctly. - Write component tests with TestBed. - Use component harnesses for Material / custom UI components. - Mock HttpClient via provideHttpClientTesting + HttpTestingController. - Test signal-based inputs with componentRef.setInput(). - Test NgRx Effects with provideMockActions. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing patterns broadly (see angular-routing — covers testing routes briefly). - Form patterns broadly (see angular-forms).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Shared conventions for every SDLC development-phase architect agent: hard rules, code quality bar, workflow steps (superpowers invocation, spec reading, codebase exploration, verification), and the report/compact-summary contract. Architects load this skill first, then apply their stack-specific instructions on top.

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

AratKruglik のスキルをすべて見る

このスキルの問題を報告する