跳到主要内容
知仓学习社ZHICANG

vue-development

Vue 3 development with Composition API, reactivity system, component patterns, TypeScript integration, and best practices.

不碰外部(只输出文字)无严重或高危命中a5c-ai/babysitter

它会碰到什么

扫了多少2 个文本文件,9 KB
它会碰到什么不碰外部(只输出文字)
命中总数2 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

Vue Development Skill

Expert assistance for building Vue 3 applications with Composition API and modern patterns.

Capabilities

  • Create Vue 3 components with Composition API
  • Implement reactive state with ref and reactive
  • Build composables for reusable logic
  • Configure TypeScript with Vue
  • Set up Vue Router and navigation guards
  • Implement provide/inject for dependency injection

Usage

Invoke this skill when you need to:

  • Create Vue 3 components
  • Build composables for shared logic
  • Set up Vue project structure
  • Implement reactive patterns
  • Configure Vue with TypeScript

Inputs

| Parameter | Type | Required | Description |

|-----------|------|----------|-------------|

| componentName | string | Yes | Component name (PascalCase) |

| compositionApi | boolean | No | Use Composition API (default: true) |

| typescript | boolean | No | Use TypeScript (default: true) |

| scriptSetup | boolean | No | Use script setup (default: true) |

Configuration Example

{
  "componentName": "UserProfile",
  "compositionApi": true,
  "typescript": true,
  "scriptSetup": true,
  "features": ["props", "emits", "slots"]
}

Component Patterns

Script Setup Component

<!-- components/UserProfile.vue -->
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';

interface User {
  id: string;
  name: string;
  email: string;
  avatar: string;
}

// Props with defaults
const props = withDefaults(defineProps<{
  user: User;
  editable?: boolean;
}>(), {
  editable: false,
});

// Emits with typing
const emit = defineEmits<{
  update: [user: User];
  delete: [id: string];
}>();

// Reactive state
const isEditing = ref(false);
const editedName = ref(props.user.name);

// Computed
const initials = computed(() => {
  return props.user.name
    .split(' ')
    .map(n => n[0])
    .join('')
    .toUpperCase();
});

// Methods
function saveChanges() {
  emit('update', { ...props.user, name: editedName.value });
  isEditing.value = false;
}

// Lifecycle
onMounted(() => {
  console.log('UserProfile mounted');
});

// Expose to parent
defineExpose({
  resetForm: () => {
    editedName.value = props.user.name;
    isEditing.value = false;
  },
});
</script>

<template>
  <div class="user-profile">
    <div class="avatar">
      <img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
      <span v-else class="initials">{{ initials }}</span>
    </div>

    <div class="info">
      <template v-if="isEditing">
        <input v-model="editedName" @keyup.enter="saveChanges" />
        <button @click="saveChanges">Save</button>
        <button @click="isEditing = false">Cancel</button>
      </template>
      <template v-else>
        <h2>{{ user.name }}</h2>
        <p>{{ user.email }}</p>
        <button v-if="editable" @click="isEditing = true">Edit</button>
      </template>
    </div>

    <slot name="actions" :user="user" />
  </div>
</template>

<style scoped>
.user-profile {
  display: flex;
  gap: 1rem;
  padding: 1rem;
}

.avatar {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  overflow: hidden;
}

.initials {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: #e5e7eb;
  font-weight: bold;
}
</style>

Composable Pattern

// composables/useUser.ts
import { ref, computed, readonly } from 'vue';

interface User {
  id: string;
  name: string;
  email: string;
}

export function useUser(userId: string) {
  const user = ref<User | null>(null);
  const loading = ref(false);
  const error = ref<Error | null>(null);

  const isAuthenticated = computed(() => !!user.value);

  async function fetchUser() {
    loading.value = true;
    error.value = null;

    try {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) throw new Error('Failed to fetch user');
      user.value = await response.json();
    } catch (e) {
      error.value = e as Error;
    } finally {
      loading.value = false;
    }
  }

  async function updateUser(data: Partial<User>) {
    if (!user.value) return;

    loading.value = true;
    try {
      const response = await fetch(`/api/users/${userId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
      user.value = await response.json();
    } catch (e) {
      error.value = e as Error;
    } finally {
      loading.value = false;
    }
  }

  return {
    user: readonly(user),
    loading: readonly(loading),
    error: readonly(error),
    isAuthenticated,
    fetchUser,
    updateUser,
  };
}

// composables/useLocalStorage.ts
import { ref, watch } from 'vue';

export function useLocalStorage<T>(key: string, defaultValue: T) {
  const stored = localStorage.getItem(key);
  const data = ref<T>(stored ? JSON.parse(stored) : defaultValue);

  watch(
    data,
    (newValue) => {
      localStorage.setItem(key, JSON.stringify(newValue));
    },
    { deep: true }
  );

  return data;
}

Provide/Inject Pattern

// context/theme.ts
import { provide, inject, ref, type Ref, type InjectionKey } from 'vue';

type Theme = 'light' | 'dark';

interface ThemeContext {
  theme: Ref<Theme>;
  toggleTheme: () => void;
}

const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

export function provideTheme() {
  const theme = ref<Theme>('light');

  function toggleTheme() {
    theme.value = theme.value === 'light' ? 'dark' : 'light';
  }

  provide(ThemeKey, { theme, toggleTheme });

  return { theme, toggleTheme };
}

export function useTheme() {
  const context = inject(ThemeKey);
  if (!context) {
    throw new Error('useTheme must be used within a theme provider');
  }
  return context;
}

Vue Router Setup

// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: () => import('@/layouts/DefaultLayout.vue'),
    children: [
      {
        path: '',
        name: 'home',
        component: () => import('@/views/Home.vue'),
      },
      {
        path: 'dashboard',
        name: 'dashboard',
        component: () => import('@/views/Dashboard.vue'),
        meta: { requiresAuth: true },
      },
    ],
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/Login.vue'),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

// Navigation guard
router.beforeEach((to, from, next) => {
  const isAuthenticated = !!localStorage.getItem('token');

  if (to.meta.requiresAuth && !isAuthenticated) {
    next({ name: 'login', query: { redirect: to.fullPath } });
  } else {
    next();
  }
});

export default router;

Best Practices

  • Use script setup for cleaner components
  • Create composables for reusable logic
  • Type props and emits properly
  • Use readonly for exposed reactive state
  • Leverage provide/inject for deep dependency passing

Target Processes

  • vue-application-development
  • vue-component-library
  • nuxt-full-stack
  • frontend-architecture

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

它属于哪个仓库

星标★ 1,796
本站分层T1
该仓技能数2115
原文件路径library/specializations/web-development/skills/vue-development/SKILL.md

同一个仓库里的其他技能

看这个仓库的全部 2115 个技能