# 贡献指南 (Contributing Guide) 欢迎来到 **KVideo** 项目!我们非常感谢你愿意为这个项目做出贡献。无论是修复 Bug、添加新功能、改进文档,还是提出建议,你的每一份贡献都将让这个项目变得更好。 为了确保协作顺畅、代码质量一致,请在提交贡献前仔细阅读本指南。 ## 📋 目录 - [行为准则](#行为准则) - [快速开始](#快速开始) - [开发环境设置](#开发环境设置) - [代码规范](#代码规范) - [Git 工作流程](#git-工作流程) - [提交规范](#提交规范) - [Pull Request 指南](#pull-request-指南) - [设计系统规范](#设计系统规范) - [测试要求](#测试要求) - [常见问题](#常见问题) ## 🤝 行为准则 我们致力于构建一个开放、友好、包容的社区环境。请在参与项目时: - ✅ 保持尊重和礼貌 - ✅ 欢迎不同的观点和经验 - ✅ 接受建设性的批评 - ✅ 专注于对社区最有利的事情 - ❌ 不要使用性别化的语言或图像 - ❌ 不要进行人身攻击或政治攻击 - ❌ 不要骚扰或歧视他人 详细的行为准则请参阅 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。 ## 🚀 快速开始 ### 我能贡献什么? 以下是一些你可以做出贡献的方式: 1. **🐛 报告 Bug**:发现了问题?请提交 Issue 2. **💡 提出新功能**:有好想法?在 Discussions 或 Issues 中分享 3. **📝 改进文档**:发现文档不清晰或有错误?帮助我们改进 4. **🎨 优化 UI/UX**:让界面更美观、更易用 5. **⚡ 性能优化**:让应用运行得更快 6. **🔧 修复 Bug**:解决现有的问题 7. **✨ 添加功能**:实现新的特性 ### 第一次贡献? 如果这是你第一次为开源项目做贡献,我们推荐: 1. 浏览 [GitHub Issues](https://github.com/KuekHaoYang/KVideo/issues) 2. 寻找标记为 `good first issue` 的问题 3. 在 Issue 中评论,表明你想要解决这个问题 4. 按照本指南进行开发和提交 ## 🛠 开发环境设置 ### 系统要求 确保你的开发环境满足以下要求: | 工具 | 最低版本 | 推荐版本 | 检查命令 | |------|----------|----------|----------| | **Node.js** | 20.0.0 | 22.x LTS | `node --version` | | **npm** | 9.0.0 | 10.x | `npm --version` | | **Git** | 2.30.0 | 最新版本 | `git --version` | ### 详细设置步骤 #### 1. Fork 仓库 点击 GitHub 页面右上角的 "Fork" 按钮,将项目 Fork 到你的账号下。 #### 2. 克隆仓库 ```bash # 克隆你 Fork 的仓库 git clone https://github.com/YOUR_USERNAME/KVideo.git cd KVideo # 添加上游仓库 git remote add upstream https://github.com/KuekHaoYang/KVideo.git ``` #### 3. 安装依赖 ```bash npm install ``` #### 4. 启动开发服务器 ```bash npm run dev ``` 访问 `http://localhost:3000` 查看应用。 #### 5. 验证环境 确保以下命令都能正常运行: ```bash # 代码检查 npm run lint # 构建测试 npm run build ``` ## 📏 代码规范 ### 核心规范 #### 1. 文件长度限制 ⚠️ > [!CAUTION] > **这是项目的硬性规则!所有项目文件必须保持在 150 行以内(除系统文件外)。** **检查命令:** ```bash find . -type f -not -path "*/node_modules/*" -not -path "*/.next/*" -not -path "*/.git/*" -not -name "package-lock.json" -not -name "*.png" -not -name "*.md" | xargs wc -l | awk '$1 > 150 && $2 != "total" {print $2 " - " $1 "行"}' ``` **如果命令有输出,说明有文件超过 150 行,必须重构!** **重构策略:** 如果文件超过 150 行,请使用以下方法重构: ##### A. 提取组件 **问题:** 一个组件太长,包含太多 JSX **解决方案:** 将大组件拆分为多个小组件 ```typescript // ❌ 不好:一个 200 行的大组件 export function VideoPlayer() { // 150+ 行代码 return (
{/* 大量 JSX */}
); } // ✅ 好:拆分为多个小组件 export function VideoPlayer() { return (
); } // PlayerControls.tsx (单独文件) export function PlayerControls() { /* ... */ } // ProgressBar.tsx (单独文件) export function ProgressBar() { /* ... */ } // VolumeControl.tsx (单独文件) export function VolumeControl() { /* ... */ } ``` ##### B. 提取自定义 Hook **问题:** 组件包含大量状态逻辑 **解决方案:** 将逻辑提取到自定义 Hook ```typescript // ❌ 不好:组件内有大量状态逻辑 export function SearchPage() { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); // ... 大量逻辑 const handleSearch = async () => { // ... 50+ 行逻辑 }; return
{/* JSX */}
; } // ✅ 好:提取到自定义 Hook export function SearchPage() { const { query, results, loading, handleSearch } = useSearch(); return
{/* JSX */}
; } // useSearch.ts (单独文件) export function useSearch() { // ... 所有状态逻辑 return { query, results, loading, handleSearch }; } ``` ##### C. 提取工具函数 **问题:** 文件包含大量辅助函数 **解决方案:** 将工具函数移到 `lib/utils/` ```typescript // ❌ 不好:组件文件包含工具函数 export function VideoCard() { const formatDuration = (seconds: number) => { // ... 格式化逻辑 }; const formatDate = (date: Date) => { // ... 格式化逻辑 }; // ... 更多工具函数 return
{/* JSX */}
; } // ✅ 好:提取到工具文件 import { formatDuration, formatDate } from '@/lib/utils/format-utils'; export function VideoCard() { return
{/* JSX */}
; } // lib/utils/format-utils.ts export function formatDuration(seconds: number) { /* ... */ } export function formatDate(date: Date) { /* ... */ } ``` ##### D. 模块化 **问题:** 单个文件处理多个相关功能 **解决方案:** 按功能拆分文件并使用桶文件(barrel exports) ```typescript // ❌ 不好:player-utils.ts 包含 200 行 export function parseHLS() { /* ... */ } export function handlePlayback() { /* ... */ } export function manageQuality() { /* ... */ } // ... 更多函数 // ✅ 好:拆分为多个文件 // lib/utils/player/index.ts export * from './hls-parser'; export * from './playback-manager'; export * from './quality-manager'; // lib/utils/player/hls-parser.ts export function parseHLS() { /* ... */ } // lib/utils/player/playback-manager.ts export function handlePlayback() { /* ... */ } // lib/utils/player/quality-manager.ts export function manageQuality() { /* ... */ } ``` #### 2. TypeScript 规范 **类型安全** ```typescript // ❌ 避免使用 any function processData(data: any) { return data.value; } // ✅ 使用具体类型 interface VideoData { id: string; title: string; url: string; } function processData(data: VideoData) { return data.title; } // ✅ 或使用 unknown(需要类型检查) function processData(data: unknown) { if (typeof data === 'object' && data !== null && 'value' in data) { return (data as { value: string }).value; } throw new Error('Invalid data'); } ``` **函数返回类型** ```typescript // ❌ 缺少返回类型 function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); } // ✅ 明确返回类型 function calculateTotal(items: Item[]): number { return items.reduce((sum, item) => sum + item.price, 0); } ``` **接口定义** ```typescript // ✅ 使用 interface 定义对象类型 interface VideoCardProps { video: Video; onPlay: (id: string) => void; className?: string; } // ✅ 使用 type 定义联合类型 type ThemeMode = 'light' | 'dark' | 'system'; ``` #### 3. React 组件规范 **函数组件** ```typescript // ✅ 标准函数组件结构 interface ButtonProps { variant?: 'primary' | 'secondary'; children: React.ReactNode; onClick?: () => void; } export function Button({ variant = 'primary', children, onClick }: ButtonProps) { return ( ); } ``` **组件文件组织** ```typescript // 1. 导入 import React from 'react'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; // 2. 类型定义 interface ComponentProps { // ... } // 3. 组件定义 export function Component({ prop1, prop2 }: ComponentProps) { // 4. Hooks const [state, setState] = useState(); const router = useRouter(); // 5. 事件处理函数 const handleClick = () => { // ... }; // 6. 渲染 return (
{/* JSX */}
); } ``` **单一职责原则** ```typescript // ❌ 组件做太多事情 export function VideoSection() { // 获取数据 // 处理搜索 // 渲染列表 // 处理分页 // 处理过滤 } // ✅ 拆分为专注的组件 export function VideoSection() { const videos = useVideos(); return (
); } ``` #### 4. 样式规范 **Tailwind CSS 优先** ```typescript // ✅ 使用 Tailwind 类名 export function Card({ children }: { children: React.ReactNode }) { return (
{children}
); } ``` **遵循 Liquid Glass 设计系统** ```typescript // ✅ 正确使用圆角
{/* 容器:大圆角 */}
{/* 小元素:完全圆形 */} // ❌ 不要使用其他圆角值
{/* 错误! */}
{/* 错误! */} ``` **响应式设计** ```typescript // ✅ 移动优先的响应式设计
``` #### 5. 命名规范 **文件命名** - 组件文件:`PascalCase.tsx`(例如:`VideoCard.tsx`) - Hook 文件:`camelCase.ts`(例如:`useVideoPlayer.ts`) - 工具文件:`kebab-case.ts`(例如:`format-utils.ts`) - 类型文件:`kebab-case.ts`(例如:`video-types.ts`) **变量命名** ```typescript // ✅ 清晰的命名 const videoList = [...]; const isLoading = false; const handleSubmit = () => {}; // ❌ 模糊的命名 const data = [...]; const flag = false; const fn = () => {}; ``` **常量命名** ```typescript // ✅ 全大写 + 下划线 const MAX_VIDEO_DURATION = 7200; const API_BASE_URL = 'https://api.example.com'; ``` #### 6. 导入顺序 ```typescript // 1. React 和 Next.js import React from 'react'; import { useState } from 'react'; import Link from 'next/link'; // 2. 第三方库 import { create } from 'zustand'; // 3. 项目别名导入 import { Button } from '@/components/ui/Button'; import { formatDate } from '@/lib/utils/date-utils'; // 4. 相对路径导入 import { LocalComponent } from './LocalComponent'; // 5. 类型导入 import type { Video } from '@/lib/types/video'; ``` ## 🔄 Git 工作流程 ### 分支策略 **主分支** - `main`:稳定的生产分支,只接受 PR 合并 **功能分支命名** 遵循以下命名规范: - `feat/功能名称`:新功能(例如:`feat/add-playlist`) - `fix/问题描述`:错误修复(例如:`fix/search-crash`) - `docs/文档修改`:文档更新(例如:`docs/update-readme`) - `refactor/重构名称`:代码重构(例如:`refactor/player-controls`) - `perf/优化内容`:性能优化(例如:`perf/image-loading`) - `style/样式修改`:样式调整(例如:`style/button-spacing`) - `test/测试内容`:测试相关(例如:`test/add-unit-tests`) - `chore/其他修改`:构建或工具变动(例如:`chore/update-deps`) ### 开发流程 #### 1. 同步上游仓库 在开始新工作前,先同步最新的代码: ```bash # 获取上游更新 git fetch upstream # 切换到主分支 git checkout main # 合并上游更新 git merge upstream/main # 推送到你的 Fork git push origin main ``` #### 2. 创建功能分支 ```bash # 从 main 创建新分支 git checkout -b feat/your-feature-name # 确认当前分支 git branch ``` #### 3. 进行开发 在开发过程中: - 频繁提交小的、原子性的改动 - 编写清晰的提交信息 - 定期运行 `npm run lint` 检查代码 #### 4. 提交前检查 **必须通过的检查:** ```bash # 1. 代码规范检查 npm run lint # 2. 文件长度检查 find . -type f -not -path "*/node_modules/*" -not -path "*/.next/*" -not -path "*/.git/*" -not -name "package-lock.json" -not -name "*.png" -not -name "*.md" | xargs wc -l | awk '$1 > 150 && $2 != "total" {print $2 " - " $1 "行"}' # 3. 构建测试 npm run build ``` **如果任何检查失败,必须先修复!** #### 5. 推送分支 ```bash # 推送到你的 Fork git push origin feat/your-feature-name ``` ## 📝 提交规范 ### Conventional Commits 我们使用 [Conventional Commits](https://www.conventionalcommits.org/) 规范: ``` ():