关于 frontend-development
这项前端开发指南 Skill 覆盖 React、Vue 3、Svelte 5 和 Angular,帮助开发者选择模式并构建组件、路由、数据获取及类型化应用结构,提供各框架专属指导。
主要功能
- 01比较四种前端框架
- 02指导组件与功能目录结构
- 03涵盖路由和数据获取
- 04应用 TypeScript 与性能实践
- 05提供框架专属模式
适用场景
- 01规划新的前端项目
- 02构建组件或应用功能
- 03改进数据获取与路由
- 04评估框架迁移方案
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samhvw8/ dotfiles --skill frontend-development安装后在 Claude Code 中输入/frontend-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
resources/common-patterns.mdcomplete-examples.mdcomponent-patterns.mddata-fetching.mdfile-organization.mdloading-and-error-states.mdperformance.mdrouting-guide.mdstyling-guide.mdtypescript-standards.md详细介绍
前端开发指南
目的
这是一份全面的现代前端开发指南,涵盖 React、Vue 3、Svelte 5 和 Angular。内容包括框架专属模式、常见架构原则和跨框架最佳实践。
何时使用此技能
- 在 React、Vue、Svelte 或 Angular 中创建组件或页面
- 使用框架专属模式开发新功能
- 实现状态管理(Pinia、Zustand、stores、signals)
- 设置路由(TanStack Router、Vue Router、SvelteKit、Angular Router)
- 使用数据获取模式(TanStack Query、composables、SvelteKit load 函数)
- 跨框架优化性能
- 组合和复用组件
- 集成 TypeScript 并遵循最佳实践
- 为项目选择合适的框架
- 在框架之间迁移
框架选择指南
根据项目需求选择框架:
| 框架 | 最适合 | 学习曲线 | 性能 | 生态系统 |
|---|---|---|---|---|
| React | 大型应用、强类型、企业级项目 | 中等 | 良好 | 最大 |
| Vue 3 | 渐进式采用、易于上手 | 低 | 优秀 | 不断壮大 |
| Svelte 5 | 小型/中型应用、原生体验 | 低 | 最佳 | 较小 |
| Angular | 企业级项目、功能全面 | 陡峭 | 良好 | 完备 |
快速决策:
- 已有代码库? 使用现有框架,或规划迁移
- 团队经验? 利用现有知识
- 项目规模? 大型 → React/Angular,中型 → Vue/React,小型 → Svelte/Vue
- 性能要求高? Svelte > Vue > React ≈ Angular
- 需要 TypeScript? 都能很好地支持(Angular 集成最佳)
- 生态系统需求? React > Vue > Angular > Svelte
详细模式请参阅下方的框架专属章节。
快速比较框架
| 特性 | React | Vue 3 | Svelte 5 | Angular |
|---|---|---|---|---|
| 响应性 | Hooks(useState) | ref/reactive | Runes($state) | Signals |
| 组件 | JSX/TSX | SFC (.vue) | SFC (.svelte) | Decorators/Class |
| 状态管理 | Zustand/Context | Pinia | Stores | Services |
| 路由 | React Router/TanStack | Vue Router | SvelteKit | Angular Router |
| 数据获取 | TanStack Query | Composables/VueQuery | Load 函数 | HttpClient/RxJS |
| 样式 | CSS-in-JS/Modules | Scoped CSS | Scoped CSS | 组件样式 |
| 全栈 | Next.js | Nuxt | SvelteKit | Universal/SSR |
| 包体积 | ~40KB | ~32KB | ~3KB | ~60KB |
| 编译器 | 运行时 | 运行时 | 编译时 | AOT 编译器 |
跳转到:
- React 开发 - Hooks、Suspense、TanStack 生态系统
- Vue 3 开发 - Composition API、Pinia、Nuxt
- Svelte 5 开发 - Runes、SvelteKit、最小运行时
- Angular 开发 - Signals、独立组件、RxJS
- 框架无关模式 - 通用概念
React 开发
新组件检查清单
要创建组件?请遵循此检查清单:
- 使用带 TypeScript 的
React.FC<Props>模式 - 如果组件较重,进行懒加载:
React.lazy(() => import()) - 加载状态使用
<SuspenseLoader>包裹 - 使用
useSuspenseQuery获取数据 - 导入别名:
@/、~types、~components、~features - 样式:少于 100 行时内联,超过 100 行时使用单独文件
- 对传递给子组件的事件处理函数使用
useCallback - 在底部添加默认导出
- 加载指示器不要使用提前返回
- 使用
useMuiSnackbar通知用户
新功能检查清单
创建新功能?请按以下结构设置:
- 创建
features/{feature-name}/目录 - 创建子目录:
api/、components/、hooks/、helpers/、types/ - 创建 API 服务文件:
api/{feature}Api.ts - 在
types/中设置 TypeScript 类型 - 在
routes/{feature-name}/index.tsx中创建路由 - 懒加载功能组件
- 使用 Suspense 边界
- 从功能的
index.ts导出公共 API
导入别名速查
| 别名 | 解析为 | 示例 |
|---|---|---|
@/ | src/ | import { apiClient } from '@/lib/apiClient' |
~types | src/types | import type { User } from '~types/user' |
~components | src/components | import { SuspenseLoader } from '~components/SuspenseLoader' |
~features | src/features | import { authApi } from '~features/auth' |
定义于:vite.config.ts 第 180-185 行
常用导入速查表
// React & Lazy Loading
import React, { useState, useCallback, useMemo } from 'react';
const Heavy = React.lazy(() => import('./Heavy'));
// MUI Components
import { Box, Paper, Typography, Button, Grid } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';
// TanStack Query (Suspense)
import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query';
// TanStack Router
import { createFileRoute } from '@tanstack/react-router';
// Project Components
import { SuspenseLoader } from '~components/SuspenseLoader';
// Hooks
import { useAuth } from '@/hooks/useAuth';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
// Types
import type { Post } from '~types/post';
主题指南
🎨 组件模式
现代 React 组件使用:
React.FC<Props>,确保类型安全React.lazy(),实现代码拆分SuspenseLoader,处理加载状态- 命名常量 + 默认导出模式
核心概念:
- 懒加载较重的组件(DataGrid、图表、编辑器)
- 始终将懒加载组件包裹在 Suspense 中
- 使用 SuspenseLoader 组件(带淡入淡出动画)
- 组件结构:Props → Hooks → 处理函数 → 渲染 → 导出
📖 完整指南:resources/component-patterns.md
📊 数据获取
主要模式:useSuspenseQuery
- 与 Suspense 边界配合使用
- 缓存优先策略(调用 API 前先检查网格缓存)
- 替代
isLoading检查 - 使用泛型确保类型安全
API 服务层:
- 创建
features/{feature}/api/{feature}Api.ts - 使用
apiClientaxios 实例 - 按功能集中管理方法
- 路由格式:
/form/route(而非/api/form/route)
📖 完整指南:resources/data-fetching.md
📁 文件组织
features/ 与 components/:
features/:特定领域(帖子、评论、身份验证)components/:真正可复用的组件(SuspenseLoader、CustomAppBar)
功能子目录:
features/
my-feature/
api/ # API service layer
components/ # Feature components
hooks/ # Custom hooks
helpers/ # Utility functions
types/ # TypeScript types
📖 完整指南:resources/file-organization.md
🎨 样式
内联样式与单独文件:
- <100 行:内联
const styles: Record<string, SxProps<Theme>> -
100 行:单独的
.styles.ts文件
主要方法:
- 对 MUI 组件使用
sx属性 - 使用
SxProps<Theme>确保类型安全 - 访问主题:
(theme) => theme.palette.primary.main
MUI v7 Grid:
<Grid size={{ xs: 12, md: 6 }}> // ✅ v7 syntax
<Grid xs={12} md={6}> // ❌ Old syntax
📖 完整指南:resources/styling-guide.md
🛣️ 路由
TanStack Router - 基于文件夹:
- 目录:
routes/my-route/index.tsx - 懒加载组件
- 使用
createFileRoute - 在 loader 中添加面包屑数据
示例:
import { createFileRoute } from '@tanstack/react-router';
import { lazy } from 'react';
const MyPage = lazy(() => import('@/features/my-feature/components/MyPage'));
export const Route = createFileRoute('/my-route/')({
component: MyPage,
loader: () => ({ crumb: 'My Route' }),
});
📖 完整指南:resources/routing-guide.md
⏳ 加载与错误状态
关键规则:不要提前返回
// ❌ NEVER - Causes layout shift
if (isLoading) {
return <LoadingSpinner />;
}
// ✅ ALWAYS - Consistent layout
<SuspenseLoader>
<Content />
</SuspenseLoader>
**原因:**可防止累积布局偏移(CLS),改善用户体验
错误处理:
- 使用
useMuiSnackbar向用户反馈 - 切勿使用
react-toastify - 使用 TanStack Query 的
onError回调
📖 完整指南:resources/loading-and-error-states.md
⚡ 性能
优化模式:
useMemo:开销较大的计算(筛选、排序、映射)useCallback:传递给子组件的事件处理函数React.memo:开销较大的组件- 防抖搜索(300-500ms)
- 防止内存泄漏(在 useEffect 中清理)
📖 完整指南:resources/performance.md
📘 TypeScript
规范:
- 严格模式,不使用
any类型 - 函数显式声明返回类型
- 类型导入:
import type { User } from '~types/user' - 组件属性接口需包含 JSDoc
📖 完整指南:resources/typescript-standards.md
🔧 常见模式
涵盖主题:
- 使用 Zod 验证的 React Hook Form
- DataGrid 包装器契约
- 对话框组件规范
- 用于获取当前用户的
useAuthhook - 带缓存失效的 mutation 模式
📖 完整指南:resources/common-patterns.md
📚 完整示例
完整可运行示例:
- 涵盖所有模式的现代组件
- 完整的功能结构
- API 服务层
- 带延迟加载的路由
- Suspense + useSuspenseQuery
- 带验证的表单
📖 完整指南:resources/complete-examples.md
React 专属导航指南
| 需求 | 阅读此资源 |
|---|---|
| 创建 React 组件 | component-patterns.md |
| 使用 TanStack Query 获取数据 | data-fetching.md |
| 整理文件/文件夹 | file-organization.md |
| 使用 MUI v7 设置样式 | styling-guide.md |
| 设置 TanStack Router | routing-guide.md |
| 处理加载/错误状态 | loading-and-error-states.md |
| 优化 React 性能 | performance.md |
| TypeScript 类型 | typescript-standards.md |
| 表单/身份验证/DataGrid | common-patterns.md |
| 查看完整 React 示例 | complete-examples.md |
**注意:**以上资源专为 React 编写。有关 Vue/Svelte/Angular,请参阅本文档中的框架章节。
核心原则(适用于所有框架)
- 延迟加载大型组件:路由、数据网格、图表、编辑器、模态框
- 统一加载状态:通过恰当的加载 UI 避免布局偏移
- 类型安全:所有组件和 API 均使用 TypeScript
- 组件组合:采用规模小、专注且职责明确的组件
- 单向数据流:属性向下传递,事件向上传递
- 性能优先:对开销较大的计算进行记忆化,为列表使用虚拟滚动
- 功能组织有序:将相关代码分组(组件、状态、类型、工具)
- 遵循框架惯用模式:使用特定框架的模式(hooks、composables、runes、服务)
React 专属原则
- useSuspenseQuery:新代码的首选数据获取模式
- 不使用提前返回:避免布局偏移(使用 Suspense 边界)
- 导入别名:使用 @/、~types、~components、~features
- 根据规模选择样式:<100 行内编写,>100 行拆分到单独文件
Vue 专属原则
- Composition API:优先于 Options API,以便复用
- Script Setup:使用它以获得更简洁的语法和更好的性能
- Composables:像 React hooks 一样提取可复用逻辑
- 使用 Pinia 管理状态:支持 TypeScript 的现代 Vuex 替代方案
Svelte 专属原则
- 使用 Runes 实现响应性:使用 $state、$derived、$effect(Svelte 5+)
- 使用 SvelteKit 构建应用:利用基于文件的路由和服务端函数
- 尽量少用 Stores:Runes 可减少组件对 Stores 的需求
- 渐进增强:支持无 JS 运行,并通过 JS 增强
Angular 专属原则
- 独立组件:自 v17 起为默认方式(避免使用 NgModules)
- 使用 Signals 管理状态:简单的响应式场景优先于 RxJS
- OnPush 检测:优化变更检测
- 依赖注入:使用 inject() 实现现代 DI
快速参考:文件结构
src/
features/
my-feature/
api/
myFeatureApi.ts # API service
components/
MyFeature.tsx # Main component
SubComponent.tsx # Related components
hooks/
useMyFeature.ts # Custom hooks
useSuspenseMyFeature.ts # Suspense hooks
helpers/
myFeatureHelpers.ts # Utilities
types/
index.ts # TypeScript types
index.ts # Public exports
components/
SuspenseLoader/
SuspenseLoader.tsx # Reusable loader
CustomAppBar/
CustomAppBar.tsx # Reusable app bar
routes/
my-route/
index.tsx # Route component
create/
index.tsx # Nested route
现代组件模板(快速复制)
import React, { useState, useCallback } from 'react';
import { Box, Paper } from '@mui/material';
import { useSuspenseQuery } from '@tanstack/react-query';
import { featureApi } from '../api/featureApi';
import type { FeatureData } from '~types/feature';
interface MyComponentProps {
id: number;
onAction?: () => void;
}
export const MyComponent: React.FC<MyComponentProps> = ({ id, onAction }) => {
const [state, setState] = useState<string>('');
const { data } = useSuspenseQuery({
queryKey: ['feature', id],
queryFn: () => featureApi.getFeature(id),
});
const handleAction = useCallback(() => {
setState('updated');
onAction?.();
}, [onAction]);
return (
<Box sx={{ p: 2 }}>
<Paper sx={{ p: 3 }}>
{/* Content */}
</Paper>
</Box>
);
};
export default MyComponent;
完整示例请参阅 resources/complete-examples.md
Vue 3 开发
组件模式
组合式 API - 使用 <script setup> 的现代 Vue 3 方法:
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { User } from '@/types/user'
interface Props {
userId: number
}
const props = defineProps<Props>()
const emit = defineEmits<{
update: [user: User]
}>()
const user = ref<User | null>(null)
const isLoading = ref(true)
const displayName = computed(() => user.value?.name ?? 'Unknown')
onMounted(async () => {
user.value = await fetchUser(props.userId)
isLoading.value = false
})
function handleUpdate() {
if (user.value) emit('update', user.value)
}
</script>
<template>
<div class="user-profile">
<div v-if="isLoading">Loading...</div>
<div v-else>
<h2>{{ displayName }}</h2>
<button @click="handleUpdate">Update</button>
</div>
</div>
</template>
<style scoped>
.user-profile {
padding: 1rem;
}
</style>
关键模式:
<script setup>- 简洁的组合式 API 语法defineProps<T>()- 使用泛型实现类型安全的 propsdefineEmits<T>()- 类型安全的事件ref()、reactive()- 响应式基础 APIcomputed()- 派生状态watch()、watchEffect()- 副作用onMounted、onUnmounted- 生命周期钩子
状态管理(Pinia)
// stores/userStore.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// State
const users = ref<User[]>([])
const currentUser = ref<User | null>(null)
// Getters
const userCount = computed(() => users.value.length)
const isAuthenticated = computed(() => currentUser.value !== null)
// Actions
async function fetchUsers() {
users.value = await api.getUsers()
}
function setCurrentUser(user: User) {
currentUser.value = user
}
return { users, currentUser, userCount, isAuthenticated, fetchUsers, setCurrentUser }
})
// Component usage
<script setup lang="ts">
import { useUserStore } from '@/stores/userStore'
const userStore = useUserStore()
</script>
路由(Vue Router)
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('@/views/HomeView.vue')
},
{
path: '/users/:id',
component: () => import('@/views/UserView.vue'),
props: true
}
]
})
// Component with route params
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const userId = route.params.id
function goBack() {
router.push('/')
}
</script>
数据获取模式
组合式函数 - 可复用逻辑:
// composables/useUser.ts
import { ref, type Ref } from 'vue'
export function useUser(id: Ref<number>) {
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
async function fetchUser() {
loading.value = true
try {
user.value = await api.getUser(id.value)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
watchEffect(() => {
fetchUser()
})
return { user, loading, error, refetch: fetchUser }
}
// Usage in component
<script setup lang="ts">
const props = defineProps<{ userId: number }>()
const { user, loading, error } = useUser(toRef(props, 'userId'))
</script>
Nuxt.js 服务端:
<script setup lang="ts">
// Nuxt auto-imports composables
const { data: user, pending, error } = await useFetch(`/api/users/${route.params.id}`)
</script>
Vue 3 最佳实践
- 优先使用组合式 API,而非选项式 API - 更好地支持 TypeScript,且可复用性更强
- Script setup - 减少样板代码,自动注册
- 优先使用 Pinia,而非 Vuex - API 更简单,更好地支持 TypeScript,并兼容组合式 API
- 组合式函数 - 提取可复用逻辑(类似 React hooks)
- v-memo - 优化开销大的渲染(类似 React.memo)
- defineOptions - 在 script setup 中设置组件选项
- 浅层响应式 - 对大型对象使用
shallowRef()
Svelte 5 开发
基于 Runes 的响应式
Svelte 5 Runes - 新的响应式系统:
<script lang="ts">
import type { User } from '$lib/types/user'
interface Props {
userId: number
onUpdate?: (user: User) => void
}
let { userId, onUpdate }: Props = $props()
// Reactive state with $state
let user = $state<User | null>(null)
let isLoading = $state(true)
// Derived state with $derived
let displayName = $derived(user?.name ?? 'Unknown')
let userAge = $derived.by(() => {
if (!user?.birthDate) return null
return calculateAge(user.birthDate)
})
// Effects with $effect
$effect(() => {
// Runs when userId changes
loadUser(userId)
})
async function loadUser(id: number) {
isLoading = true
user = await fetchUser(id)
isLoading = false
}
function handleUpdate() {
if (user) onUpdate?.(user)
}
</script>
{#if isLoading}
<div>Loading...</div>
{:else if user}
<div class="user-profile">
<h2>{displayName}</h2>
{#if userAge}
<p>Age: {userAge}</p>
{/if}
<button onclick={handleUpdate}>Update</button>
</div>
{/if}
<style>
.user-profile {
padding: 1rem;
}
</style>
Svelte 5 Runes:
$state()- 响应式状态(替代用于实现响应性的let)$derived- 计算值(替代$:)$derived.by()- 复杂的派生状态$effect()- 副作用(替代$:语句)$props()- 使用解构的组件 props$bindable()- props 的双向绑定$inspect()- 调试响应式值
状态管理(Svelte Stores)
// stores/user.ts
import { writable, derived, readonly } from 'svelte/store'
function createUserStore() {
const { subscribe, set, update } = writable<User[]>([])
return {
subscribe,
setUsers: (users: User[]) => set(users),
addUser: (user: User) => update(users => [...users, user]),
removeUser: (id: number) => update(users => users.filter(u => u.id !== id)),
reset: () => set([])
}
}
export const users = createUserStore()
export const userCount = derived(users, $users => $users.length)
// Component usage (Svelte 4 style)
<script>
import { users } from '$lib/stores/user'
</script>
<p>Total users: {$users.length}</p>
// Or with runes (Svelte 5)
<script>
import { users } from '$lib/stores/user'
let currentUsers = $state($users)
</script>
SvelteKit 路由与数据加载
// src/routes/users/[id]/+page.ts
import type { PageLoad } from './$types'
export const load: PageLoad = async ({ params, fetch }) => {
const user = await fetch(`/api/users/${params.id}`).then(r => r.json())
return {
user
}
}
// src/routes/users/[id]/+page.svelte
<script lang="ts">
import type { PageData } from './$types'
let { data }: { data: PageData } = $props()
let { user } = $derived(data)
</script>
<h1>{user.name}</h1>
SvelteKit 模式:
+page.svelte- 页面组件+page.ts- 页面数据加载(在服务端和客户端运行)+page.server.ts- 仅在服务端运行的加载函数+layout.svelte- 共享布局+server.ts- API 端点- 表单操作 - 渐进增强
Svelte 5 最佳实践
- 使用 Runes - 现代响应式(Svelte 5+)
- 显式响应式 - 使用
$state(),而非隐式的let - 优先使用 $derived,而非 $: - 意图更清晰,优化效果更好
- 组件组合 - 使用插槽和代码片段
- 使用 SvelteKit 构建应用 - 支持路由和 SSR 的全栈框架
- 尽量减少 stores - 在组件中,Runes 可减少对 stores 的需求
- 渐进增强 - 使用表单操作改善 UX
Angular 开发
独立组件(现代)
// user-profile.component.ts
import { Component, Input, Output, EventEmitter, signal, computed } from '@angular/core'
import { CommonModule } from '@angular/common'
import type { User } from '@/types/user'
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule],
template: `
<div class="user-profile" *ngIf="!isLoading(); else loading">
<h2>{{ displayName() }}</h2>
<p>Age: {{ userAge() }}</p>
<button (click)="handleUpdate()">Update</button>
</div>
<ng-template #loading>
<div>Loading...</div>
</ng-template>
`,
styles: [`
.user-profile {
padding: 1rem;
}
`]
})
export class UserProfileComponent {
@Input({ required: true }) userId!: number
@Output() userUpdate = new EventEmitter<User>()
// Signals - reactive primitives
user = signal<User | null>(null)
isLoading = signal(true)
// Computed signals
displayName = computed(() => this.user()?.name ?? 'Unknown')
userAge = computed(() => {
const birthDate = this.user()?.birthDate
return birthDate ? this.calculateAge(birthDate) : null
})
async ngOnInit() {
this.user.set(await this.fetchUser(this.userId))
this.isLoading.set(false)
}
handleUpdate() {
const currentUser = this.user()
if (currentUser) this.userUpdate.emit(currentUser)
}
}
Angular Signals - 新的响应式系统(v16+):
signal()- 可写的响应式值computed()- 派生状态effect()- 副作用.set()、.update()- 修改 signal 值()- 读取 signal 值(作为函数调用)
状态管理(Services + Signals)
// services/user.service.ts
import { Injectable, signal, computed } from '@angular/core'
import { HttpClient } from '@angular/common/http'
@Injectable({ providedIn: 'root' })
export class UserService {
private users = signal<User[]>([])
private currentUser = signal<User | null>(null)
// Public computed signals
readonly userCount = computed(() => this.users().length)
readonly isAuthenticated = computed(() => this.currentUser() !== null)
constructor(private http: HttpClient) {}
async fetchUsers() {
const users = await this.http.get<User[]>('/api/users').toPromise()
this.users.set(users)
}
setCurrentUser(user: User) {
this.currentUser.set(user)
}
}
// Component usage
export class MyComponent {
constructor(public userService: UserService) {}
// Access in template
// {{ userService.userCount() }}
}
路由(Angular Router)
// app.routes.ts
import { Routes } from '@angular/router'
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component').then(m => m.HomeComponent)
},
{
path: 'users/:id',
loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent)
}
]
// Component with route params
import { Component } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
export class UserDetailComponent {
userId = signal<string>('')
constructor(
private route: ActivatedRoute,
private router: Router
) {
this.userId.set(this.route.snapshot.paramMap.get('id') ?? '')
}
goBack() {
this.router.navigate(['/'])
}
}
数据获取(RxJS + Signals)
import { Component, signal } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { toSignal } from '@angular/core/rxjs-interop'
export class UserListComponent {
private http = inject(HttpClient)
// Convert Observable to Signal
users = toSignal(this.http.get<User[]>('/api/users'), { initialValue: [] })
// Or manual signal management
manualUsers = signal<User[]>([])
async loadUsers() {
const users = await this.http.get<User[]>('/api/users').toPromise()
this.manualUsers.set(users)
}
}
Angular 最佳实践
- 独立组件 - 自 v17 起为默认方式(无需 NgModules)
- 优先使用 Signals,而非 RxJS - 更简单的状态响应式处理
- toSignal - 在需要时将 Observables 转换为 Signals
- inject() - 现代依赖注入(无需构造函数)
- 延迟加载 - 路由使用
loadComponent - OnPush 变更检测 - 优化渲染
- 类型化表单 - 使用
FormGroup<T>确保类型安全
框架无关模式
状态管理对比
| 模式 | React | Vue 3 | Svelte 5 | Angular |
|---|---|---|---|---|
| 本地状态 | useState | ref() | $state() | signal() |
| 派生状态 | useMemo | computed() | $derived | computed() |
| 副作用 | useEffect | watch/watchEffect | $effect() | effect() |
| 全局状态 | Zustand/Context | Pinia | Stores | Services |
| 异步状态 | TanStack Query | VueQuery/Composables | Stores | RxJS/Signals |
组件组合模式
Props/Events 模式(所有框架):
- 父组件通过 props 向下传递数据
- 子组件向上触发事件
- 单向数据流
Slots/Children 模式:
// React
<Layout>
<Header />
<Content />
</Layout>
// Vue
<Layout>
<template #header><Header /></template>
<template #content><Content /></template>
</Layout>
// Svelte
<Layout>
<Header slot="header" />
<Content slot="content" />
</Layout>
// Angular
<app-layout>
<app-header header></app-header>
<app-content content></app-content>
</app-layout>
路由模式
基于文件的路由:
- Next.js(React)、Nuxt(Vue)、SvelteKit(Svelte)
- 文件夹结构定义路由
- 使用
[id]表示动态片段
编程式路由:
- React Router、Vue Router、Angular Router
- 在配置中定义路由
- 更灵活,但也更冗长
性能优化
通用技术:
- 代码拆分 - 延迟加载路由和大型组件
- 记忆化 - 缓存开销较大的计算
- 虚拟滚动 - 仅渲染可见项目
- 防抖 - 降低开销较大的操作频率(搜索、调整大小)
- 图像优化 - 延迟加载、响应式图像、现代格式
- 包分析 - 找出并移除大型依赖项
框架特定技术:
- React:
React.memo、useMemo、useCallback、Suspense - Vue:
v-memo、shallowRef、markRaw、KeepAlive - Svelte: 自动优化、
$derived、极简运行时 - Angular: OnPush、signals、trackBy、纯管道
相关技能
重要:对于 UI/UX 设计工作,请调用专用技能:
Skill("ui-ux-pro-max") → UI/UX design, visual hierarchy, color theory, spacing, typography
UI/设计:
- ui-ux-pro-max:UI/UX 设计原则、视觉层次、色彩理论、间距系统、排版。设计决策、美化 UI、选择颜色/字体和布局设计时,请使用此技能。
- ui-design-system:组件设计系统、无障碍设计、设计令牌
- ui-styling:Tailwind CSS、适用于所有框架的样式模式
后端集成:
- backend-development:前端使用的 API 模式
- better-auth:身份验证实现(适用于所有框架)
全栈框架:
- web-frameworks:深入了解 Next.js(React)、Nuxt(Vue)、SvelteKit(Svelte)
开发:
- debugging:与框架无关的调试策略
- refactoring-expert:代码质量和重构模式
- chrome-devtools:浏览器调试、性能分析
何时调用 ui-ux-pro-max
遇到以下情况时,调用 Skill("ui-ux-pro-max"):
- 设计新的 UI 组件或页面
- 选择调色板、排版或间距
- 改进视觉层次或布局
- 让界面显得专业、现代、整洁
- 用户提到:难看、不一致、杂乱、需要设计帮助
- 构建仪表板、管理面板或 SaaS 界面
技能状态:覆盖多种框架,并支持渐进式披露