frontend-development
36
总安装量
22
周安装量
#10467
全站排名
安装命令
npx skills add https://github.com/aaaaqwq/claude-code-skills --skill frontend-development
Agent 安装分布
claude-code
15
codex
13
gemini-cli
13
opencode
13
antigravity
12
Skill 文档
å端页é¢å¼å
åè½è¯´æ
æ¤æè½ä¸é¨ç¨äºå端 Web å¼åï¼å æ¬ï¼
- ç°ä»£åç«¯æ¡æ¶å¼åï¼ReactãVueãAngularï¼
- ååºå¼é¡µé¢å¸å±
- 交äºåè½å®ç°
- ç¶æç®¡ç
- æ§è½ä¼å
- å端工ç¨å
使ç¨åºæ¯
- “å建ä¸ä¸ª React ç»å½é¡µé¢”
- “å®ç°ä¸ä¸ªååºå¼å¯¼èªæ ”
- “ä¼å页é¢å è½½æ§è½”
- “éæç¬¬ä¸æ¹ API”
- “å®ç°æ·±è²æ¨¡å¼å梔
- “å建å¯å¤ç¨ç UI ç»ä»¶åº”
ææ¯æ
æ ¸å¿æ¡æ¶
- Reactï¼ç»ä»¶åãHooksãContext
- Vueï¼ååºå¼ãç»åå¼ APIãPinia
- Angularï¼TypeScriptãä¾èµæ³¨å ¥ãRxJS
- Next.jsï¼SSRãSSGãAPI Routes
- Nuxt.jsï¼Vue SSR æ¡æ¶
UI æ¡æ¶
- Tailwind CSSï¼å®ç¨ä¼å ç CSS æ¡æ¶
- Material-UIï¼React Material Design
- Ant Designï¼ä¼ä¸çº§ UI ç»ä»¶åº
- Element Plusï¼Vue 3 ç»ä»¶åº
- Chakra UIï¼å¯è®¿é®çç»ä»¶ç³»ç»
ç¶æç®¡ç
- Reduxï¼å¯é¢æµçç¶æå®¹å¨
- Zustandï¼è½»éçº§ç¶æç®¡ç
- Piniaï¼Vue ç¶æç®¡ç
- MobXï¼ååºå¼ç¶æç®¡ç
- Jotaiï¼åååç¶æç®¡ç
æå»ºå·¥å ·
- Viteï¼ä¸ä¸ä»£å端æå»ºå·¥å ·
- Webpackï¼æ¨¡åæå å¨
- Turbopackï¼Rust 驱å¨çæå å¨
- esbuildï¼æé JavaScript æå å¨
å¼å工使µç¨
项ç®åå§å
- å建项ç®ï¼ä½¿ç¨èææ¶å·¥å ·
- é ç½®ç¯å¢ï¼ESLintãPrettierãTypeScript
- ç®å½ç»æï¼ç»ç»ä»£ç æä»¶
- ä¾èµå®è£ ï¼å®è£ å¿ è¦çå
- Git åå§åï¼çæ¬æ§å¶è®¾ç½®
å¼åæµç¨
- éæ±åæï¼çè§£åè½éæ±
- ç»ä»¶è®¾è®¡ï¼æåç»ä»¶ç»æ
- æ ·å¼å¼åï¼å®ç° UI 设计
- åè½å®ç°ï¼ç¼åä¸å¡é»è¾
- æµè¯éªè¯ï¼åå æµè¯åéææµè¯
- 代ç 审æ¥ï¼å¢é Code Review
- é¨ç½²ä¸çº¿ï¼æå»ºååå¸
æä½³å®è·µ
代ç ç»ç»
src/
âââ components/ # å¯å¤ç¨ç»ä»¶
â âââ Button/
â âââ Input/
â âââ Modal/
âââ pages/ # 页é¢ç»ä»¶
â âââ Home/
â âââ Login/
â âââ Dashboard/
âââ hooks/ # èªå®ä¹ Hooks
âââ utils/ # å·¥å
·å½æ°
âââ services/ # API æå¡
âââ store/ # ç¶æç®¡ç
âââ styles/ # å
¨å±æ ·å¼
âââ types/ # TypeScript ç±»å
âââ constants/ # 常éå®ä¹
ç»ä»¶è®¾è®¡åå
- åä¸èè´£ï¼ä¸ä¸ªç»ä»¶åªåä¸ä»¶äº
- å¯å¤ç¨æ§ï¼è®¾è®¡éç¨çç»ä»¶
- å¯ç»åæ§ï¼å°ç»ä»¶ç»åæå¤§ç»ä»¶
- Props éªè¯ï¼ä½¿ç¨ TypeScript æ PropTypes
- é»è®¤å¼ï¼æä¾åççé»è®¤å±æ§
æ§è½ä¼å
- 代ç åå²ï¼å¨æå¯¼å ¥åæå è½½
- èææ»å¨ï¼å¤ç大å表
- é²æèæµï¼ä¼åäºä»¶å¤ç
- Memo åï¼é¿å ä¸å¿ è¦ç鿏²æ
- å¾çä¼åï¼æå è½½ãWebP æ ¼å¼
- ç¼åçç¥ï¼åç使ç¨ç¼å
å¯è®¿é®æ§
- è¯ä¹å HTMLï¼ä½¿ç¨æ£ç¡®çæ ç¾
- é®ç导èªï¼æ¯æé®çæä½
- ARIA 屿§ï¼è¾ å©ææ¯æ¯æ
- 对æ¯åº¦ï¼ç¡®ä¿æåå¯è¯»æ§
- ç¦ç¹ç®¡çï¼æ¸ æ°çç¦ç¹æç¤º
代ç 示ä¾
React ç»ä»¶ç¤ºä¾
import React, { useState } from 'react';
import styled from 'styled-components';
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}
const StyledButton = styled.button<ButtonProps>`
padding: ${props => {
switch (props.size) {
case 'small': return '8px 16px';
case 'large': return '16px 32px';
default: return '12px 24px';
}
}};
background: ${props =>
props.variant === 'primary' ? '#2196F3' : '#757575'
};
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
&:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-2px);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'medium',
disabled = false,
onClick,
children
}) => {
return (
<StyledButton
variant={variant}
size={size}
disabled={disabled}
onClick={onClick}
>
{children}
</StyledButton>
);
};
Vue ç»ä»¶ç¤ºä¾
<template>
<button
:class="buttonClasses"
:disabled="disabled"
@click="handleClick"
>
<slot />
</button>
</template>
<script setup lang="ts">
import { computed } from 'vue';
interface Props {
variant?: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'medium',
disabled: false
});
const emit = defineEmits<{
click: [];
}>();
const buttonClasses = computed(() => [
'btn',
`btn-${props.variant}`,
`btn-${props.size}`,
{ 'btn-disabled': props.disabled }
]);
const handleClick = () => {
if (!props.disabled) {
emit('click');
}
};
</script>
<style scoped>
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background: #2196F3;
color: white;
}
.btn-secondary {
background: #757575;
color: white;
}
.btn-small {
padding: 8px 16px;
font-size: 14px;
}
.btn-large {
padding: 16px 32px;
font-size: 18px;
}
.btn:hover:not(.btn-disabled) {
opacity: 0.9;
transform: translateY(-2px);
}
.btn-disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
èªå®ä¹ Hook 示ä¾
import { useState, useEffect } from 'react';
interface FetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
export function useFetch<T>(url: string): FetchState<T> {
const [state, setState] = useState<FetchState<T>>({
data: null,
loading: true,
error: null
});
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
setState(prev => ({ ...prev, loading: true }));
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (!cancelled) {
setState({ data, loading: false, error: null });
}
} catch (error) {
if (!cancelled) {
setState({
data: null,
loading: false,
error: error as Error
});
}
}
};
fetchData();
return () => {
cancelled = true;
};
}, [url]);
return state;
}
ç¶æç®¡ç示ä¾ï¼Zustandï¼
import create from 'zustand';
import { persist } from 'zustand/middleware';
interface User {
id: string;
name: string;
email: string;
}
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
login: (user: User, token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
isAuthenticated: false,
login: (user, token) =>
set({ user, token, isAuthenticated: true }),
logout: () =>
set({ user: null, token: null, isAuthenticated: false })
}),
{
name: 'auth-storage'
}
)
);
ååºå¼è®¾è®¡
æç¹ç³»ç»
/* Mobile First */
/* Mobile: é»è®¤æ ·å¼ */
.container {
padding: 16px;
}
/* Tablet: >= 768px */
@media (min-width: 768px) {
.container {
padding: 24px;
}
}
/* Desktop: >= 1024px */
@media (min-width: 1024px) {
.container {
padding: 32px;
max-width: 1200px;
margin: 0 auto;
}
}
/* Large Desktop: >= 1440px */
@media (min-width: 1440px) {
.container {
max-width: 1400px;
}
}
Flexbox å¸å±
.flex-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
@media (max-width: 768px) {
.flex-container {
flex-direction: column;
}
}
Grid å¸å±
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
padding: 24px;
}
æµè¯çç¥
åå æµè¯ï¼Jest + React Testing Libraryï¼
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('renders correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByText('Click me')).toBeDisabled();
});
});
E2E æµè¯ï¼Playwrightï¼
import { test, expect } from '@playwright/test';
test('user can login', async ({ page }) => {
await page.goto('http://localhost:3000/login');
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('http://localhost:3000/dashboard');
await expect(page.locator('h1')).toContainText('Dashboard');
});
é¨ç½²åä¼å
æå»ºä¼å
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
ui: ['@mui/material']
}
}
},
minify: 'terser',
terserOptions: {
compress: {
drop_console: true
}
}
}
});
ç¯å¢åé
# .env.production
VITE_API_URL=https://api.production.com
VITE_APP_NAME=My App
Windowsä¸Viteè¿ç¨ç®¡çï¼éè¦ï¼ï¼
é®é¢ï¼Windowsç¯å¢ä¸Viteå¼åæå¡å¨éåºæ¶ï¼chokidaræä»¶çå¬å¨å¯è½ä¸ä¼æ£ç¡®æ¸ çï¼å¯¼è´åµå°¸è¿ç¨å ç¨ç«¯å£ã
è§£å³æ¹æ¡ï¼
- ä¼åvite.config.jsé ç½®
export default defineConfig({
server: {
host: '0.0.0.0',
port: 5173,
strictPort: false,
watch: { usePolling: false, interval: 1000 }
},
optimizeDeps: { exclude: ['@vueup/vue-quill'] }
})
- åå»ºæ¸ çèæ¬ stop-dev.bat
@echo off
echo Killing Vite processes...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :5173 2^>nul ^| findstr LISTENING') do (
taskkill /F /PID %%a 2>nul
)
echo Done!
- å建æºè½å¯å¨èæ¬ dev.bat
@echo off
echo Starting Vite dev server...
cmd /c npm run dev
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :5173 2^>nul ^| findstr LISTENING') do (
taskkill /F /PID %%a 2>nul
)
echo Vite stopped
ä½¿ç¨æ¹æ³ï¼ä½¿ç¨dev.batå¯å¨ï¼stop-dev.batæ¸
çï¼é¿å
åå°è¿è¡ã
注æäºé¡¹
- éµå¾ªå¢é代ç è§è
- ç¼åæ¸ æ°ç注éåææ¡£
- èèæµè§å¨å ¼å®¹æ§
- 注æå®å ¨é®é¢ï¼XSSãCSRFï¼
- ä¼åé¦å±å è½½æ¶é´
- å®ç°é误边çåéçº§æ¹æ¡
- ä½¿ç¨ TypeScript æé«ä»£ç è´¨é
â ï¸ ææ¡£åæ¥è§èï¼é常éè¦ï¼ï¼
å端å¼å宿åï¼å¿ é¡»åæ¥æ´æ°ç¸å ³ææ¡£ï¼ä¿æä»£ç ä¸ææ¡£ä¸è´ï¼
å¿ é¡»ç»´æ¤çææ¡£
1. è¿åº¦ææ¡£ (docs/frontend/progress.md)
è®°å½å¼åè¿åº¦ã已宿åè½ãå¾ åäºé¡¹ã
æ´æ°æ¶æºï¼
- 宿æ°é¡µé¢/ç»ä»¶æ¶
- ä¿®æ¹ææ¯æ æ¶
- 宿éç¨ç¢æ¶
ææ¡£æ ¼å¼åèï¼
# å端å¼åè¿åº¦
> æåæ´æ°ï¼YYYY-MM-DD
> ç¶æï¼å¼åä¸ (X%宿)
## 已宿åè½
### æ ¸å¿æ¶æ
| 模å | ç¶æ | 说æ |
|------|------|------|
| 项ç®åå§å | â
| Vite + Vue3 + Pinia |
## å¾
宿åè½
### ä¼å
级 P1
- [ ] æ·±è²æ¨¡å¼
## 代ç ç»è®¡
| ç±»å | æ°é |
|------|------|
| 页é¢ç»ä»¶ | 8 |
2. APIå¯¹æ¥ææ¡£ (docs/frontend/api-reference.md)
è®°å½å端APIæ¥å£ãè¯·æ±æ ¼å¼ãååºæ ¼å¼ã
æ´æ°æ¶æºï¼
- å端æ°å¢/ä¿®æ¹APIæ¶
- å端è°ç¨æ°APIæ¶
- APIåæ®µååæ¶
å¿ é¡»å å«ï¼
- æå¡å°åé ç½®
- éç¨ååºæ ¼å¼
- æ¯ä¸ªAPIç详ç»è¯´æï¼æ¹æ³ãè·¯å¾ãåæ°ãååºç¤ºä¾ï¼
- å端è°ç¨ç¤ºä¾ä»£ç
3. README.md (frontend/README.md)
项ç®å ¥å£ææ¡£ï¼å¿«é䏿æåã
å¿ é¡»å å«ï¼
- 项ç®ç®ä»
- ææ¯æ çæ¬
- å¿«éå¯å¨å½ä»¤
- ç®å½ç»æ
- å¼å注æäºé¡¹
- ä¸å端ç对æ¥è¯´æ
ææ¡£åæ¥æ£æ¥æ¸ å
å端å¼å宿åï¼é®èªå·±ï¼
- ææ´æ°äº
docs/frontend/progress.mdåï¼ - æ°APIè°ç¨è®°å½å¨
docs/frontend/api-reference.mdäºåï¼ - README.md éçææ¯æ çæ¬æ¯ææ°çåï¼
- 妿æç¯å¢åéååï¼æ´æ°ææ¡£äºåï¼
- 代ç ç»æåååï¼æ´æ°ç®å½ç»æäºåï¼
ææ¡£ä¸ä»£ç 忥åå
- å æ´æ°ææ¡£åæäº¤ä»£ç – ç¡®ä¿ææ¡£åæ ææ°ç¶æ
- APIååç«å³åæ¥ – å端APIåäºï¼åç«¯ææ¡£ä¹è¦æ´æ°
- é ç½®ååè®°å½å¨æ¡ – ç¯å¢åéã端å£çé ç½®ååè¦åè¿ææ¡£
- 宿Reviewææ¡£ – æ¯å¨æ£æ¥ä¸æ¬¡ææ¡£æ¯å¦è¿æ¶
- å 餿»é¾æ¥ – ä¸åå¨çåè½ä»ææ¡£ä¸ç§»é¤
ææ¡£å½åè§è
| ææ¡£ç±»å | è·¯å¾ | å½åæ ¼å¼ |
|---|---|---|
| å端è¿åº¦ | docs/frontend/progress.md |
åºå®æä»¶å |
| APIåè | docs/frontend/api-reference.md |
åºå®æä»¶å |
| å端è¿åº¦ | docs/backend/progress.md |
åºå®æä»¶å |
| å端APIåè | docs/backend/api-reference.md |
åºå®æä»¶å |
| ææ¯è®¾è®¡ | docs/plans/YYYY-MM-DD-*.md |
ææ¥æå½å |