# 程式碼規範與開發標準
## 概述
建立統一的程式碼撰寫規範和開發流程標準,確保團隊協作效率和代碼品質。
**技術堆疊**:
- **前端**: 原生Web技術 (HTML5 + CSS3/SCSS + Modern JavaScript ES2022+)
- **後端**: .NET Core API
- **建置工具**: Vite 5.x
**最後更新**: 2025-09-10
## 通用開發原則
### 代碼品質原則
- [ ] **可讀性優先**: 代碼應該容易閱讀和理解
- [ ] **一致性**: 遵循統一的命名和格式規範
- [ ] **簡潔性**: 避免過度複雜的解決方案
- [ ] **可測試性**: 代碼結構便於單元測試
- [ ] **可維護性**: 考慮未來修改和擴展的便利性
### SOLID原則遵循
- [ ] **單一職責**: 每個函數/類只負責一個明確的功能
- [ ] **開放封閉**: 對擴展開放,對修改封閉
- [ ] **里氏替換**: 子類應該能夠替換父類
- [ ] **介面隔離**: 不應該依賴不需要的介面
- [ ] **依賴倒置**: 依賴抽象而非具體實現
## C# (.NET Core) 規範
### 基本格式規則
#### EditorConfig 配置
```ini
# .editorconfig
root = true
[*]
charset = utf-8
end_of_line = crlf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
[*.{json,js,ts,tsx,css,scss,yml,yaml}]
indent_size = 2
```
#### .NET 分析器規則
```xml
net8.0
enable
true
CS1591
true
all
runtime; build; native; contentfiles; analyzers
```
## HTML5 原生前端規範
### HTML 結構標準
#### 語義化標記規則
```html
單字介紹
confidence
/ˈkɒnfɪdəns/
信心;自信心;把握
```
#### 無障礙標準
```html
跳至主要內容
```
### CSS/SCSS 規範
#### 組織結構
```scss
// styles/main.scss - 主要樣式入口
@import 'base/reset';
@import 'base/variables';
@import 'base/typography';
@import 'components/vocabulary-card';
@import 'components/mode-selector';
@import 'pages/vocabulary';
@import 'utilities/helpers';
```
#### 變數命名和組織
```scss
// base/variables.scss
// ✅ 語義化變數命名
$color-primary-teal: #00e5cc;
$color-secondary-purple: #8b5cf6;
$color-success-green: #22c55e;
$color-error-red: #ef4444;
$color-warning-yellow: #f59e0b;
$color-text-primary: #1f2937;
$color-text-secondary: #6b7280;
$color-text-tertiary: #9ca3af;
$spacing-xs: 0.25rem; // 4px
$spacing-sm: 0.5rem; // 8px
$spacing-md: 1rem; // 16px
$spacing-lg: 1.5rem; // 24px
$spacing-xl: 2rem; // 32px
$font-size-xs: 0.75rem; // 12px
$font-size-sm: 0.875rem; // 14px
$font-size-base: 1rem; // 16px
$font-size-lg: 1.125rem; // 18px
$font-size-xl: 1.25rem; // 20px
// ❌ 避免的命名
$blue: #00e5cc; // 太泛化
$s: 0.5rem; // 太簡短
```
#### BEM 方法論
```scss
// ✅ 使用BEM命名慣例
.vocabulary-card {
// Block
padding: $spacing-lg;
&__header {
// Element
border-bottom: 1px solid $color-divider;
}
&__word {
// Element
font-size: $font-size-xl;
font-weight: 700;
}
&--featured {
// Modifier
border: 2px solid $color-primary-teal;
}
&--learning {
// Modifier
background: rgba($color-warning-yellow, 0.1);
}
}
// 使用方式
//
//
//
```
### Modern JavaScript ES2022+ 規範
#### 模組系統
```javascript
// ✅ ES6+ 模組語法
// modules/vocabulary/index.js
export class VocabularyModule {
constructor() {
this.state = new VocabularyState()
this.api = new VocabularyAPI()
}
async init() {
await this.loadInitialData()
this.setupEventListeners()
}
}
// utils/helpers.js
export const debounce = (func, wait) => {
let timeout
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
export const formatPhonetic = (phonetic) => {
return phonetic.startsWith('/') ? phonetic : `/${phonetic}/`
}
// main.js
import { VocabularyModule } from './modules/vocabulary/index.js'
import { debounce } from './utils/helpers.js'
```
#### 現代JavaScript特性
```javascript
// ✅ 使用現代語法特性
class VocabularyState {
// 私有屬性 (ES2022)
#words = new Map()
#currentWord = null
constructor() {
this.progress = 0
}
// Getter/Setter
get currentWord() {
return this.#currentWord
}
set currentWord(word) {
this.#currentWord = word
this.notifySubscribers()
}
// 異步方法
async loadVocabulary(lessonId) {
try {
const response = await fetch(`/api/vocabulary/${lessonId}`)
const data = await response.json()
// 使用解構賦值
const { words, metadata } = data
// 使用 Map 和現代陣列方法
words.forEach(word => {
this.#words.set(word.id, {
...word,
learned: false,
attempts: 0
})
})
return { success: true, count: words.length }
} catch (error) {
console.error('載入詞彙失敗:', error)
throw new VocabularyError('無法載入詞彙資料')
}
}
// 使用可選鏈 (Optional Chaining)
getWordProgress(wordId) {
return this.#words.get(wordId)?.attempts ?? 0
}
// 使用 Nullish Coalescing
getWordDefinition(wordId, fallback = '無定義') {
return this.#words.get(wordId)?.definition ?? fallback
}
}
// 自定義錯誤類
class VocabularyError extends Error {
constructor(message, code = 'VOCABULARY_ERROR') {
super(message)
this.name = 'VocabularyError'
this.code = code
}
}
```
#### 事件處理和DOM操作
```javascript
// ✅ 現代事件處理
class VocabularyUI {
constructor() {
this.elements = {
wordCard: document.querySelector('.vocabulary-card'),
playButton: document.querySelector('.play-audio-btn'),
nextButton: document.querySelector('.next-word-btn')
}
this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
}
setupEventListeners() {
// 使用委派事件處理
document.addEventListener('click', this.handleGlobalClick.bind(this))
// 鍵盤快捷鍵
document.addEventListener('keydown', (event) => {
const shortcuts = {
'Space': () => this.playAudio(),
'Enter': () => this.nextWord(),
'KeyR': () => this.repeatWord(),
'Escape': () => this.exitLesson()
}
const handler = shortcuts[event.code]
if (handler && !event.target.matches('input, textarea')) {
event.preventDefault()
handler()
}
})
}
handleGlobalClick(event) {
// 使用現代選擇器
if (event.target.matches('.play-audio-btn')) {
const wordId = event.target.dataset.word
this.playWordAudio(wordId)
}
if (event.target.closest('.vocabulary-card')) {
const card = event.target.closest('.vocabulary-card')
this.highlightCard(card)
}
}
// 使用 Web Audio API
async playWordAudio(word) {
try {
const audioUrl = `/api/audio/pronunciation/${word}`
const response = await fetch(audioUrl)
const audioBuffer = await response.arrayBuffer()
const audioData = await this.audioContext.decodeAudioData(audioBuffer)
const source = this.audioContext.createBufferSource()
source.buffer = audioData
source.connect(this.audioContext.destination)
source.start()
} catch (error) {
console.error('音頻播放失敗:', error)
this.showErrorMessage('無法播放發音')
}
}
}
### 命名規範
#### C# 命名慣例
```csharp
// ✅ 類別和方法使用PascalCase
public class UserService
{
public async Task GetUserProfileAsync(Guid userId)
{
// 方法實現
}
public decimal CalculateMonthlyInterestRate(decimal principal, decimal rate)
{
return principal * rate / 12;
}
}
// ✅ 變數和參數使用camelCase
private readonly IUserRepository _userRepository;
private const int MaxRetryAttempts = 3;
public async Task ValidateUserAsync(string email, string password)
{
var isValidEmail = IsValidEmailFormat(email);
var hashedPassword = HashPassword(password);
return isValidEmail && await _userRepository.ValidateCredentialsAsync(email, hashedPassword);
}
// ❌ 避免的命名
private string data; // 太泛化
private int u; // 太簡短
private async Task GetUserProfileDataAsync() {} // 冗餘的Data後綴
```
## Vite 5.x 建置配置
### vite.config.js 標準配置
```javascript
// vite.config.js
import { defineConfig } from 'vite'
import { resolve } from 'path'
export default defineConfig({
// 開發伺服器配置
server: {
port: 3000,
host: true,
open: true
},
// 建置配置
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: true,
minify: 'terser',
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
vocabulary: resolve(__dirname, 'pages/vocabulary.html'),
dialogue: resolve(__dirname, 'pages/dialogue.html')
}
}
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "./src/styles/base/variables.scss";`
}
}
},
// 解析配置
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@styles': resolve(__dirname, 'src/styles'),
'@components': resolve(__dirname, 'src/components')
}
}
})
```
### 專案結構標準
```
web-frontend/
├── index.html # 主頁面
├── vite.config.js # Vite 配置
├── package.json # 依賴管理
├── public/
│ ├── favicon.svg # 網站圖示
│ └── assets/ # 靜態資源
├── src/
│ ├── styles/ # 樣式檔案
│ │ ├── main.scss # 主樣式入口
│ │ ├── base/ # 基礎樣式
│ │ ├── components/ # 組件樣式
│ │ └── pages/ # 頁面樣式
│ ├── scripts/ # JavaScript 模組
│ │ ├── main.js # 應用入口
│ │ ├── modules/ # 功能模組
│ │ └── utils/ # 工具函數
│ └── components/ # 可重用組件HTML
└── pages/ # 各頁面HTML檔案
├── vocabulary.html
├── dialogue.html
└── profile.html
```
## 程式碼品質工具
### ESLint 配置
```javascript
// .eslintrc.js
export default {
env: {
browser: true,
es2022: true
},
extends: [
'eslint:recommended'
],
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module'
},
rules: {
// 代碼風格
'indent': ['error', 2],
'quotes': ['error', 'single'],
'semi': ['error', 'never'],
// 最佳實踐
'no-console': 'warn',
'no-unused-vars': 'error',
'prefer-const': 'error',
'no-var': 'error'
}
}
```
### Prettier 配置
```json
// .prettierrc
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "avoid"
}
```
### Stylelint 配置
```javascript
// stylelint.config.js
export default {
extends: [
'stylelint-config-standard-scss'
],
rules: {
'selector-class-pattern': '^[a-z][a-z0-9]*(-[a-z0-9]+)*$',
'scss/at-import-partial-extension': null,
'property-no-vendor-prefix': null
}
}
```
## 版本控制規範
### Git 提交訊息格式
```
():