# 程式碼規範與開發標準 ## 概述 建立統一的程式碼撰寫規範和開發流程標準,確保團隊協作效率和代碼品質。 **技術堆疊**: - **前端**: 原生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 提交訊息格式 ``` ():