單位轉換器技術實作

深入了解單位轉換器的實作原理,包含多類別轉換系統、實時計算引擎和響應式用戶介面

概述

單位轉換器是一個功能強大的線上工具,專為開發者和工程師設計,能夠在多種單位類別間進行精確轉換。 本工具採用純前端架構,完全在瀏覽器端運行,零網路傳輸,確保數據的絕對隱私和安全性。 提供實時計算、多類別支援、精度控制、歷史記錄、常用轉換快捷鍵等專業功能。

🔒 隱私保護承諾

所有單位轉換處理都在您的瀏覽器本地進行,數據不會上傳到任何伺服器,確保您的敏感資料完全安全。

技術架構與核心設計

整體架構設計

技術項目 實作方式 設計考量
前端框架 純 JavaScript (ES6+) 零依賴,最小化載入時間
轉換引擎 數學計算 + 轉換係數表 高精度計算與實時響應
多類別系統 動態配置 + 模組化設計 支援9大類別,易於擴展
精度控制 動態小數位數調整 0-6位精度可選
用戶介面 響應式設計 + 雙語系統 跨設備兼容性與國際化

核心類別架構

/**
 * Unit Converter Core Class
 * 核心單位轉換器類別
 */
class UnitConverter {
    constructor() {
        // 應用狀態 / Application state
        this.currentCategory = 'length';
        this.currentPrecision = 2;
        this.currentLanguage = 'zh';
        this.isConverting = false;
        
        // DOM 元素引用 / DOM element references
        this.elements = {};
        
        // 歷史記錄 (最大50條) / History records (max 50)
        this.conversionHistory = this.loadHistory();
        
        // 初始化應用 / Initialize application
        this.init();
    }
    
    /**
     * 初始化方法 - 按順序執行所有初始化步驟
     * Initialization method - Execute all initialization steps in order
     */
    async init() {
        // 等待 DOM 加載完成 / Wait for DOM to load
        if (document.readyState === 'loading') {
            await new Promise(resolve => {
                document.addEventListener('DOMContentLoaded', resolve);
            });
        }

        // 獲取 DOM 元素引用 / Get DOM element references
        this.getElementReferences();
        
        // 載入配置和翻譯 / Load configuration and translations
        await this.loadConfig();
        
        // 初始化主題 / Initialize theme
        this.initTheme();
        
        // 設置全局語言監聽器(必須在其他初始化之前)
        // Set up global language listener (must be before other initialization)
        this.setupGlobalLanguageListener();
        
        // 設置事件監聽器 / Set up event listeners
        this.setupEventListeners();
        
        // 初始化類別選項 / Initialize category options
        this.initCategoryOptions();
        
        // 初始化單位選項 / Initialize unit options
        this.updateUnitOptions();
        
        // 更新界面文字 / Update UI text
        this.updateUIText();
    }
}

關鍵功能實現

1. 多類別轉換系統

支援長度、重量、溫度、容積、面積、時間、速度、壓力、數據大小等9大類別,每個類別包含多個單位選項。 系統採用動態配置方式,可以輕鬆添加新的類別和單位。

2. 實時計算引擎

採用 300ms 防抖機制,在用戶輸入時提供即時反饋。計算引擎支援高精度數學運算, 並提供 0-6 位小數精度控制,滿足不同場景的需求。

3. 智能歷史記錄

自動記錄最近50次轉換,避免重複記錄相同的轉換。支援點擊歷史記錄快速重用, 並提供類別標籤和時間戳記錄。

實作細節

以下是完整程式碼實作,按功能模組分類展示。點擊卡片標題可展開查看詳細程式碼:

📦 核心單位轉換器類別

UnitConverter 類別是整個應用的核心,負責管理轉換狀態、DOM 元素引用、歷史記錄和所有主要功能。 採用模組化設計,將不同功能分離到獨立的方法中,便於維護和擴展。

class UnitConverter {
    constructor() {
        // 應用狀態管理 / Application state management
        this.currentCategory = 'length';      // 當前選中的類別 / Currently selected category
        this.currentPrecision = 2;            // 小數位數精度 / Decimal precision
        this.currentLanguage = 'zh';          // 當前語言設置 / Current language setting
        this.isConverting = false;            // 轉換中標誌 / Converting flag
        
        // DOM 元素引用快取 / DOM element reference cache
        this.elements = {};
        
        // 歷史記錄管理 (最大50條) / History record management (max 50)
        this.conversionHistory = this.loadHistory();
        
        // 轉換配置數據 / Conversion configuration data
        this.categories = [];
        this.translations = {};
        this.commonConversions = {};
        
        // 防抖計時器 / Debounce timer
        this.debounceTimer = null;
        
        // 初始化應用 / Initialize application
        this.init();
    }

    /**
     * 異步初始化方法 - 確保 DOM 載入完成後再執行
     * Async initialization method - Ensure DOM is loaded before execution
     */
    async init() {
        try {
            console.log('🔄 初始化單位轉換器...');
            
            // 等待 DOM 加載完成 / Wait for DOM to load
            if (document.readyState === 'loading') {
                await new Promise(resolve => {
                    document.addEventListener('DOMContentLoaded', resolve);
                });
            }

            // 獲取 DOM 元素引用 / Get DOM element references
            this.getElementReferences();
            
            // 載入配置和翻譯 / Load configuration and translations
            await this.loadConfig();
            
            // 初始化主題 / Initialize theme
            this.initTheme();
            
            // 設置全局語言監聽器(必須在其他初始化之前)
            // Set up global language listener (must be before other initialization)
            this.setupGlobalLanguageListener();
            
            // 設置事件監聽器 / Set up event listeners
            this.setupEventListeners();
            
            // 初始化類別選項 / Initialize category options
            this.initCategoryOptions();
            
            // 初始化單位選項 / Initialize unit options
            this.updateUnitOptions();
            
            // 初始化常用轉換快捷鍵 / Initialize common conversion shortcuts
            this.initShortcuts();
            
            // 初始化最近轉換記錄 / Initialize recent conversion records
            this.updateRecentConversions();
            
            // 更新界面文字 / Update UI text
            this.updateUIText();
            
            console.log('✅ 單位轉換器初始化完成');
            
        } catch (error) {
            console.error('初始化錯誤:', error);
            this.showToast('初始化過程中發生錯誤', 'error');
        }
    }

    /**
     * 獲取所有必要的 DOM 元素引用
     * Get all necessary DOM element references
     */
    getElementReferences() {
        this.elements = {
            // 主要控件 / Main controls
            categorySelect: document.getElementById('category-select'),
            fromValue: document.getElementById('from-value'),
            toValue: document.getElementById('to-value'),
            fromUnit: document.getElementById('from-unit'),
            toUnit: document.getElementById('to-unit'),
            precisionSlider: document.getElementById('precision'),
            precisionValue: document.getElementById('precision-value'),
            
            // 按鈕 / Buttons
            swapBtn: document.getElementById('swap-btn'),
            clearBtn: document.getElementById('clear-btn'),
            copyBtn: document.getElementById('copy-btn'),
            themeToggle: document.getElementById('theme-toggle'),
            
            // 容器 / Containers
            shortcutButtons: document.getElementById('shortcut-buttons'),
            recentList: document.getElementById('recent-list'),
            
            // 標題和標籤 / Titles and labels
            pageTitle: document.getElementById('page-title'),
            categoryLabel: document.getElementById('category-label'),
            fromLabel: document.getElementById('from-label'),
            toLabel: document.getElementById('to-label'),
            fromUnitLabel: document.getElementById('from-unit-label'),
            toUnitLabel: document.getElementById('to-unit-label'),
            precisionLabel: document.getElementById('precision-label'),
            shortcutsTitle: document.getElementById('shortcuts-title'),
            recentTitle: document.getElementById('recent-title'),
            footerText: document.getElementById('footer-text')
        };
    }
}

🌐 全域語言系統整合

實現與全站語言系統的無縫整合,響應全域語言切換事件,並提供完整的中英文翻譯支援。 使用事件監聽機制確保語言切換的即時性和一致性。

/**
 * 設置全域語言監聽器 - 這是雙語系統的核心
 * Set up global language listener - This is the core of the bilingual system
 */
setupGlobalLanguageListener() {
    // 監聽全局語言切換事件 / Listen to global language switching events
    window.addEventListener('languageChanged', (event) => {
        this.currentLanguage = event.detail.language;
        this.updateLanguage();
    });
    
    // 嘗試從全局語言管理器獲取當前語言
    // Try to get current language from global language manager
    const checkGlobalLanguage = () => {
        if (window.globalI18n) {
            this.currentLanguage = window.globalI18n.currentLanguage;
            // 立即更新語言相關UI(這很重要!)
            // Immediately update language-related UI (This is important!)
            this.updateLanguage();
            return true;
        }
        return false;
    };
    
    // 立即檢查,如果沒有就延遲重試 / Check immediately, retry if not found
    if (!checkGlobalLanguage()) {
        setTimeout(() => {
            checkGlobalLanguage();
        }, 100);
    }
}

/**
 * 更新語言 - 響應語言切換事件
 * Update language - Respond to language switching events
 */
updateLanguage() {
    // 更新所有界面文字 / Update all UI text
    this.updateUIText();
    
    // 重新初始化類別選項 / Reinitialize category options
    this.initCategoryOptions();
    
    // 更新單位選項 / Update unit options
    this.updateUnitOptions();
    
    // 更新常用轉換快捷鍵 / Update common conversion shortcuts
    this.initShortcuts();
    
    // 更新最近轉換記錄 / Update recent conversion records
    this.updateRecentConversions();
}

/**
 * 更新界面文字 - 根據當前語言設置更新所有文字內容
 * Update UI text - Update all text content according to current language setting
 */
updateUIText() {
    const t = this.translations[this.currentLanguage] || this.translations.zh;
    
    // 更新主要標題和標籤 / Update main titles and labels
    if (this.elements.pageTitle) this.elements.pageTitle.textContent = t.pageTitle || '單位轉換器';
    if (this.elements.categoryLabel) this.elements.categoryLabel.textContent = t.categoryLabel || '選擇單位類別';
    if (this.elements.fromLabel) this.elements.fromLabel.textContent = t.fromLabel || '輸入數值';
    if (this.elements.toLabel) this.elements.toLabel.textContent = t.toLabel || '轉換結果';
    if (this.elements.fromUnitLabel) this.elements.fromUnitLabel.textContent = t.fromUnitLabel || '來源單位';
    if (this.elements.toUnitLabel) this.elements.toUnitLabel.textContent = t.toUnitLabel || '目標單位';
    if (this.elements.precisionLabel) this.elements.precisionLabel.textContent = t.precisionLabel || '小數位數';
    
    // 更新按鈕文字 / Update button text
    if (this.elements.clearBtn) this.elements.clearBtn.textContent = t.clearBtn || '清除';
    if (this.elements.copyBtn) this.elements.copyBtn.textContent = t.copyBtn || '複製結果';
    
    // 更新區塊標題 / Update section titles
    if (this.elements.shortcutsTitle) this.elements.shortcutsTitle.textContent = t.shortcutsTitle || '常用轉換';
    if (this.elements.recentTitle) this.elements.recentTitle.textContent = t.recentTitle || '最近使用';
    
    // 更新頁尾文字 / Update footer text
    if (this.elements.footerText) this.elements.footerText.textContent = t.footerText || '© 2024 單位轉換器 - 簡單、快速、準確';
}

/**
 * 載入配置和翻譯數據
 * Load configuration and translation data
 */
async loadConfig() {
    try {
        const response = await fetch('/api/unit-converter/config');
        const config = await response.json();
        
        this.categories = config.categories;
        this.translations = config.translations;
        this.commonConversions = config.common_conversions;
        
        console.log('✅ 配置載入完成');
    } catch (error) {
        console.error('載入配置失敗:', error);
        // 使用內建配置作為備用 / Use built-in configuration as backup
        this.useBuiltinConfig();
    }
}

🎯 事件監聽與DOM初始化

設置完整的事件監聽系統,包含類別變更、數值輸入、單位切換、精度調整等所有用戶交互事件。 採用防抖機制優化性能,避免頻繁的計算操作。

/**
 * 設置事件監聽器 - 處理所有用戶交互
 * Set up event listeners - Handle all user interactions
 */
setupEventListeners() {
    // 類別變更事件 / Category change event
    this.elements.categorySelect.addEventListener('change', () => {
        this.currentCategory = this.elements.categorySelect.value;
        this.updateUnitOptions();
        this.performConversion();
    });

    // 數值輸入事件 - 使用防抖機制 / Value input event - Use debounce mechanism
    this.elements.fromValue.addEventListener('input', () => {
        this.debounce(() => this.performConversion(), 300);
    });

    // 單位變更事件 / Unit change events
    this.elements.fromUnit.addEventListener('change', () => this.performConversion());
    this.elements.toUnit.addEventListener('change', () => this.performConversion());

    // 精度控制事件 / Precision control event
    this.elements.precisionSlider.addEventListener('input', () => {
        this.currentPrecision = parseInt(this.elements.precisionSlider.value);
        this.elements.precisionValue.textContent = this.currentPrecision;
        this.performConversion();
    });

    // 交換按鈕事件 / Swap button event
    this.elements.swapBtn.addEventListener('click', () => this.swapUnits());

    // 清除按鈕事件 / Clear button event
    this.elements.clearBtn.addEventListener('click', () => this.clearAll());

    // 複製按鈕事件 / Copy button event
    this.elements.copyBtn.addEventListener('click', () => this.copyResult());

    // 主題切換事件 / Theme toggle event
    this.elements.themeToggle.addEventListener('click', () => this.toggleTheme());

    // 快捷轉換按鈕事件(使用事件委託)
    // Quick conversion button events (using event delegation)
    this.elements.shortcutButtons.addEventListener('click', (e) => {
        if (e.target.classList.contains('shortcut-btn')) {
            const data = JSON.parse(e.target.dataset.conversion);
            this.applyShortcut(data);
        }
    });

    // 最近記錄項目事件(使用事件委託)
    // Recent record item events (using event delegation)
    this.elements.recentList.addEventListener('click', (e) => {
        if (e.target.closest('.recent-item')) {
            const data = JSON.parse(e.target.closest('.recent-item').dataset.conversion);
            this.applyRecentConversion(data);
        }
    });
}

/**
 * 初始化類別選項 - 動態生成類別下拉選單
 * Initialize category options - Dynamically generate category dropdown
 */
initCategoryOptions() {
    this.elements.categorySelect.innerHTML = '';
    
    this.categories.forEach(category => {
        const option = new Option(
            this.translations[this.currentLanguage]?.categories?.[category] || category,
            category
        );
        this.elements.categorySelect.add(option);
    });

    this.elements.categorySelect.value = this.currentCategory;
}

/**
 * 更新單位選項 - 根據選中的類別更新單位選項
 * Update unit options - Update unit options based on selected category
 */
async updateUnitOptions() {
    try {
        const response = await fetch(`/api/unit-converter/units/${this.currentCategory}`);
        const data = await response.json();

        if (data.units) {
            this.populateUnitSelects(data.units);
        }

    } catch (error) {
        console.error('載入單位失敗:', error);
    }
}

/**
 * 填充單位選擇器 - 生成單位選項
 * Populate unit selects - Generate unit options
 */
populateUnitSelects(units) {
    // 清空現有選項 / Clear existing options
    this.elements.fromUnit.innerHTML = '';
    this.elements.toUnit.innerHTML = '';

    // 添加單位選項 / Add unit options
    Object.keys(units).forEach(unitKey => {
        const unit = units[unitKey];
        const displayName = `${unit.name[this.currentLanguage] || unit.name.zh} (${unit.symbol})`;
        
        const fromOption = new Option(displayName, unitKey);
        const toOption = new Option(displayName, unitKey);
        
        this.elements.fromUnit.add(fromOption);
        this.elements.toUnit.add(toOption);
    });

    // 設置默認選項 / Set default options
    const unitKeys = Object.keys(units);
    if (unitKeys.length >= 2) {
        this.elements.fromUnit.value = unitKeys[0];
        this.elements.toUnit.value = unitKeys[1];
    }

    this.performConversion();
}

/**
 * 防抖函數 - 避免頻繁執行計算
 * Debounce function - Avoid frequent calculation execution
 */
debounce(func, wait) {
    clearTimeout(this.debounceTimer);
    this.debounceTimer = setTimeout(func, wait);
}
廣告

⚙️ 實時轉換計算引擎

核心轉換引擎負責執行實際的單位轉換計算,支援高精度數學運算和異步處理。 採用 API 調用方式確保計算的準確性和一致性。

/**
 * 執行轉換計算 - 核心轉換邏輯
 * Perform conversion calculation - Core conversion logic
 */
async performConversion() {
    // 防止重複執行 / Prevent duplicate execution
    if (this.isConverting) return;

    const value = parseFloat(this.elements.fromValue.value);
    if (isNaN(value)) {
        this.elements.toValue.value = '';
        return;
    }

    try {
        this.isConverting = true;

        // 調用轉換 API / Call conversion API
        const response = await fetch('/api/unit-converter/convert', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                value: value,
                category: this.currentCategory,
                from_unit: this.elements.fromUnit.value,
                to_unit: this.elements.toUnit.value,
                precision: this.currentPrecision
            })
        });

        const result = await response.json();

        if (result.success) {
            // 顯示轉換結果 / Display conversion result
            this.elements.toValue.value = result.formatted_result;
            
            // 添加到歷史記錄 / Add to history
            this.addToHistory(result.conversion);
        } else {
            // 顯示錯誤信息 / Display error message
            this.showToast(result.error || '轉換失敗', 'error');
        }

    } catch (error) {
        console.error('轉換錯誤:', error);
        this.showToast('轉換過程中發生錯誤', 'error');
    } finally {
        this.isConverting = false;
    }
}

/**
 * 交換單位 - 將來源單位和目標單位互換
 * Swap units - Swap source and target units
 */
swapUnits() {
    const fromUnit = this.elements.fromUnit.value;
    const toUnit = this.elements.toUnit.value;
    const fromValue = this.elements.fromValue.value;
    const toValue = this.elements.toValue.value;

    // 交換單位選擇 / Swap unit selection
    this.elements.fromUnit.value = toUnit;
    this.elements.toUnit.value = fromUnit;
    
    // 交換數值 / Swap values
    this.elements.fromValue.value = toValue;
    
    // 重新計算 / Recalculate
    this.performConversion();
    
    // 顯示提示 / Show notification
    this.showToast('已交換單位', 'success');
}

/**
 * 清除所有內容 - 重置輸入框
 * Clear all content - Reset input fields
 */
clearAll() {
    this.elements.fromValue.value = '';
    this.elements.toValue.value = '';
    this.showToast('已清除所有內容', 'success');
}

/**
 * 複製結果 - 將轉換結果複製到剪貼簿
 * Copy result - Copy conversion result to clipboard
 */
async copyResult() {
    const result = this.elements.toValue.value;
    if (!result) {
        this.showToast('沒有結果可複製', 'error');
        return;
    }

    try {
        await navigator.clipboard.writeText(result);
        this.showToast('已複製到剪貼簿!', 'success');
    } catch (error) {
        this.showToast('複製失敗,請手動複製', 'error');
    }
}

/**
 * 應用快捷轉換 - 使用預設的轉換組合
 * Apply shortcut conversion - Use preset conversion combinations
 */
applyShortcut(shortcut) {
    this.elements.categorySelect.value = shortcut.category;
    this.currentCategory = shortcut.category;
    this.updateUnitOptions().then(() => {
        this.elements.fromUnit.value = shortcut.from;
        this.elements.toUnit.value = shortcut.to;
        this.performConversion();
    });
}

/**
 * 應用最近轉換 - 重用歷史記錄
 * Apply recent conversion - Reuse history record
 */
applyRecentConversion(conversion) {
    this.elements.categorySelect.value = conversion.category;
    this.currentCategory = conversion.category;
    this.updateUnitOptions().then(() => {
        this.elements.fromUnit.value = conversion.from_unit;
        this.elements.toUnit.value = conversion.to_unit;
        this.elements.fromValue.value = conversion.value;
        this.performConversion();
    });
}

🎨 歷史記錄與快捷功能

智能歷史記錄系統自動記錄轉換歷史,避免重複記錄相同的轉換。 常用轉換快捷鍵提供快速訪問常見的轉換組合。

/**
 * 添加到歷史記錄 - 智能去重機制
 * Add to history - Smart deduplication mechanism
 */
addToHistory(conversion) {
    // 避免重複記錄 / Avoid duplicate records
    const exists = this.conversionHistory.some(item => 
        item.category === conversion.category &&
        item.from_unit === conversion.from_unit &&
        item.to_unit === conversion.to_unit &&
        Math.abs(item.value - conversion.value) < 0.0001
    );

    if (!exists) {
        // 添加新記錄到開頭 / Add new record to beginning
        this.conversionHistory.unshift({
            ...conversion,
            timestamp: Date.now()
        });

        // 限制歷史記錄數量 / Limit history record count
        if (this.conversionHistory.length > 50) {
            this.conversionHistory = this.conversionHistory.slice(0, 50);
        }

        // 保存到本地存儲 / Save to local storage
        this.saveHistory();
        
        // 更新顯示 / Update display
        this.updateRecentConversions();
    }
}

/**
 * 更新最近轉換記錄顯示
 * Update recent conversion records display
 */
updateRecentConversions() {
    this.elements.recentList.innerHTML = '';

    const recentItems = this.conversionHistory.slice(0, 10);
    if (recentItems.length === 0) {
        const t = this.translations[this.currentLanguage] || this.translations.zh;
        const noRecordText = t.messages?.noRecentConversions || '暫無最近轉換記錄';
        this.elements.recentList.innerHTML = `

${noRecordText}

`; return; } recentItems.forEach(item => { const div = document.createElement('div'); div.className = 'recent-item'; div.dataset.conversion = JSON.stringify(item); const categoryName = this.translations[this.currentLanguage]?.categories?.[item.category] || item.category; div.innerHTML = ` ${item.value} ${item.from_unit} → ${item.to_unit} ${categoryName} `; this.elements.recentList.appendChild(div); }); } /** * 初始化常用轉換快捷鍵 * Initialize common conversion shortcuts */ initShortcuts() { const shortcuts = this.commonConversions[this.currentLanguage] || []; this.elements.shortcutButtons.innerHTML = ''; shortcuts.forEach(shortcut => { const button = document.createElement('button'); button.className = 'shortcut-btn'; button.textContent = shortcut.name; button.dataset.conversion = JSON.stringify(shortcut); this.elements.shortcutButtons.appendChild(button); }); } /** * 載入歷史記錄 - 從本地存儲讀取 * Load history - Read from local storage */ loadHistory() { try { const saved = localStorage.getItem('unit-converter-history'); return saved ? JSON.parse(saved) : []; } catch (error) { console.error('載入歷史記錄失敗:', error); return []; } } /** * 保存歷史記錄 - 寫入本地存儲 * Save history - Write to local storage */ saveHistory() { try { localStorage.setItem('unit-converter-history', JSON.stringify(this.conversionHistory)); } catch (error) { console.error('保存歷史記錄失敗:', error); } } /** * 主題切換功能 - 明暗主題切換 * Theme toggle function - Light/dark theme switching */ toggleTheme() { const currentTheme = document.querySelector('.unit-converter-standalone').getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; this.setTheme(newTheme); } /** * 設置主題 - 應用指定主題 * Set theme - Apply specified theme */ setTheme(theme) { document.querySelector('.unit-converter-standalone').setAttribute('data-theme', theme); localStorage.setItem('unit-converter-theme', theme); } /** * 初始化主題 - 載入保存的主題設置 * Initialize theme - Load saved theme settings */ initTheme() { const savedTheme = localStorage.getItem('unit-converter-theme') || 'light'; this.setTheme(savedTheme); }

🛠️ 工具函數與錯誤處理

提供完整的工具函數集合,包含 Toast 通知、備用配置、錯誤處理等輔助功能。 確保應用在各種異常情況下仍能正常運行。

/**
 * 顯示 Toast 通知 - 用戶反饋系統
 * Show Toast notification - User feedback system
 */
showToast(message, type = 'success') {
    const toast = document.createElement('div');
    toast.className = `toast ${type}`;
    toast.textContent = message;
    
    document.body.appendChild(toast);
    
    // 顯示動畫 / Show animation
    setTimeout(() => toast.classList.add('show'), 100);
    
    // 隱藏並移除 / Hide and remove
    setTimeout(() => {
        toast.classList.remove('show');
        setTimeout(() => document.body.removeChild(toast), 300);
    }, 3000);
}

/**
 * 使用內建配置 - 備用配置系統
 * Use built-in configuration - Backup configuration system
 */
useBuiltinConfig() {
    // 內建配置作為備用 / Built-in configuration as backup
    this.categories = ['length', 'weight', 'temperature', 'volume', 'area', 'time', 'speed', 'pressure', 'data'];
    
    this.translations = {
        zh: {
            pageTitle: '單位轉換器',
            categoryLabel: '選擇單位類別',
            fromLabel: '輸入數值',
            toLabel: '轉換結果',
            fromUnitLabel: '來源單位',
            toUnitLabel: '目標單位',
            precisionLabel: '小數位數',
            shortcutsTitle: '常用轉換',
            recentTitle: '最近使用',
            clearBtn: '清除',
            copyBtn: '複製結果',
            footerText: '© 2024 單位轉換器 - 簡單、快速、準確',
            categories: {
                length: '長度',
                weight: '重量/質量',
                temperature: '溫度',
                volume: '容積/體積',
                area: '面積',
                time: '時間',
                speed: '速度',
                pressure: '壓力',
                data: '數據大小'
            },
            messages: {
                noRecentConversions: '暫無最近轉換記錄',
                swapUnits: '已交換單位',
                clearAll: '已清除所有內容',
                copySuccess: '已複製到剪貼簿!',
                copyError: '複製失敗,請手動複製',
                noResult: '沒有結果可複製'
            }
        },
        en: {
            pageTitle: 'Unit Converter',
            categoryLabel: 'Select Unit Category',
            fromLabel: 'Enter Value',
            toLabel: 'Conversion Result',
            fromUnitLabel: 'Source Unit',
            toUnitLabel: 'Target Unit',
            precisionLabel: 'Decimal Places',
            shortcutsTitle: 'Common Conversions',
            recentTitle: 'Recent Conversions',
            clearBtn: 'Clear',
            copyBtn: 'Copy Result',
            footerText: '© 2024 Unit Converter - Simple, Fast, Accurate',
            categories: {
                length: 'Length',
                weight: 'Weight/Mass',
                temperature: 'Temperature',
                volume: 'Volume',
                area: 'Area',
                time: 'Time',
                speed: 'Speed',
                pressure: 'Pressure',
                data: 'Data Size'
            },
            messages: {
                noRecentConversions: 'No recent conversion records',
                swapUnits: 'Units swapped',
                clearAll: 'All content cleared',
                copySuccess: 'Copied to clipboard!',
                copyError: 'Copy failed, please copy manually',
                noResult: 'No result to copy'
            }
        }
    };
    
    this.commonConversions = {
        zh: [
            {category: 'length', from: 'cm', to: 'in', name: '公分 ↔ 英吋'},
            {category: 'weight', from: 'kg', to: 'lb', name: '公斤 ↔ 磅'},
            {category: 'temperature', from: 'C', to: 'F', name: '攝氏 ↔ 華氏'},
            {category: 'volume', from: 'L', to: 'gal', name: '公升 ↔ 加侖'},
            {category: 'area', from: 'm2', to: 'ft2', name: '平方公尺 ↔ 平方英尺'},
            {category: 'speed', from: 'kmh', to: 'mph', name: '公里/小時 ↔ 英里/小時'}
        ],
        en: [
            {category: 'length', from: 'cm', to: 'in', name: 'Centimeter ↔ Inch'},
            {category: 'weight', from: 'kg', to: 'lb', name: 'Kilogram ↔ Pound'},
            {category: 'temperature', from: 'C', to: 'F', name: 'Celsius ↔ Fahrenheit'},
            {category: 'volume', from: 'L', to: 'gal', name: 'Liter ↔ Gallon'},
            {category: 'area', from: 'm2', to: 'ft2', name: 'Square Meter ↔ Square Foot'},
            {category: 'speed', from: 'kmh', to: 'mph', name: 'Kilometer/Hour ↔ Mile/Hour'}
        ]
    };
}

/**
 * 錯誤處理包裝器 - 統一錯誤處理邏輯
 * Error handling wrapper - Unified error handling logic
 */
async safeAsyncOperation(operation, errorMessage) {
    try {
        return await operation();
    } catch (error) {
        console.error(errorMessage, error);
        this.showToast(errorMessage, 'error');
        return null;
    }
}

/**
 * 輸入驗證 - 數值有效性檢查
 * Input validation - Numeric validity check
 */
validateInput(value) {
    const num = parseFloat(value);
    if (isNaN(num)) {
        return { valid: false, message: '請輸入有效的數字' };
    }
    if (!isFinite(num)) {
        return { valid: false, message: '數值超出範圍' };
    }
    return { valid: true, value: num };
}

/**
 * 格式化數字 - 根據精度設置格式化顯示
 * Format number - Format display according to precision settings
 */
formatNumber(value, precision = this.currentPrecision) {
    if (typeof value !== 'number') return '';
    
    // 處理特殊值 / Handle special values
    if (!isFinite(value)) return 'Invalid';
    
    // 根據精度格式化 / Format according to precision
    return value.toFixed(precision);
}

/**
 * 深拷貝對象 - 避免引用問題
 * Deep copy object - Avoid reference issues
 */
deepCopy(obj) {
    if (obj === null || typeof obj !== 'object') return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof Array) return obj.map(item => this.deepCopy(item));
    if (typeof obj === 'object') {
        const copy = {};
        Object.keys(obj).forEach(key => {
            copy[key] = this.deepCopy(obj[key]);
        });
        return copy;
    }
}

🚀 應用初始化與生命週期

完整的應用初始化流程和生命週期管理,確保所有組件按正確順序載入和初始化。 包含錯誤處理、回退機制和效能監控。

/**
 * 應用初始化 - 全局單例模式
 * Application initialization - Global singleton pattern
 */
let unitConverter;

/**
 * DOM 載入完成事件監聽
 * DOM content loaded event listener
 */
document.addEventListener('DOMContentLoaded', () => {
    try {
        console.log('🚀 開始初始化單位轉換器應用...');
        
        // 創建單位轉換器實例 / Create unit converter instance
        unitConverter = new UnitConverter();
        
        // 設置全局引用 / Set global reference
        window.unitConverter = unitConverter;
        
        console.log('✅ 單位轉換器應用初始化完成');
        
    } catch (error) {
        console.error('❌ 應用初始化失敗:', error);
        
        // 顯示錯誤提示 / Show error message
        const errorDiv = document.createElement('div');
        errorDiv.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #f44336;
            color: white;
            padding: 12px 20px;
            border-radius: 8px;
            z-index: 9999;
            font-family: Arial, sans-serif;
        `;
        errorDiv.textContent = '應用初始化失敗,請刷新頁面重試';
        document.body.appendChild(errorDiv);
        
        // 自動移除錯誤提示 / Auto remove error message
        setTimeout(() => {
            if (errorDiv.parentNode) {
                errorDiv.parentNode.removeChild(errorDiv);
            }
        }, 5000);
    }
});

/**
 * 窗口消息監聽 - 處理來自父頁面的消息
 * Window message listener - Handle messages from parent page
 */
window.addEventListener('message', (event) => {
    if (event.data.type === 'init' || event.data.type === 'checkHeight') {
        // 處理高度檢查請求 / Handle height check requests
        if (unitConverter) {
            // 報告當前高度 / Report current height
            const height = document.body.scrollHeight;
            if (window.parent !== window) {
                window.parent.postMessage({
                    type: 'resize',
                    height: height,
                    source: 'unit-converter'
                }, '*');
            }
        }
    }
});

/**
 * 窗口大小變化監聽 - 響應式處理
 * Window resize listener - Responsive handling
 */
window.addEventListener('resize', () => {
    if (unitConverter) {
        // 延遲執行以避免頻繁調用 / Delay execution to avoid frequent calls
        clearTimeout(unitConverter.resizeTimer);
        unitConverter.resizeTimer = setTimeout(() => {
            // 重新計算佈局 / Recalculate layout
            const height = document.body.scrollHeight;
            if (window.parent !== window) {
                window.parent.postMessage({
                    type: 'resize',
                    height: height,
                    source: 'unit-converter'
                }, '*');
            }
        }, 100);
    }
});

/**
 * 頁面卸載前處理 - 清理資源
 * Before page unload handler - Clean up resources
 */
window.addEventListener('beforeunload', () => {
    if (unitConverter) {
        // 保存當前狀態 / Save current state
        try {
            localStorage.setItem('unit-converter-state', JSON.stringify({
                category: unitConverter.currentCategory,
                precision: unitConverter.currentPrecision,
                language: unitConverter.currentLanguage,
                theme: document.querySelector('.unit-converter-standalone').getAttribute('data-theme')
            }));
        } catch (error) {
            console.error('保存狀態失敗:', error);
        }
        
        // 清理計時器 / Clean up timers
        if (unitConverter.debounceTimer) {
            clearTimeout(unitConverter.debounceTimer);
        }
        if (unitConverter.resizeTimer) {
            clearTimeout(unitConverter.resizeTimer);
        }
    }
});

/**
 * 頁面載入完成處理 - 最終初始化
 * Page load complete handler - Final initialization
 */
window.addEventListener('load', () => {
    if (unitConverter) {
        // 恢復保存的狀態 / Restore saved state
        try {
            const savedState = localStorage.getItem('unit-converter-state');
            if (savedState) {
                const state = JSON.parse(savedState);
                
                // 恢復類別選擇 / Restore category selection
                if (state.category && unitConverter.elements.categorySelect) {
                    unitConverter.elements.categorySelect.value = state.category;
                    unitConverter.currentCategory = state.category;
                }
                
                // 恢復精度設置 / Restore precision setting
                if (state.precision && unitConverter.elements.precisionSlider) {
                    unitConverter.elements.precisionSlider.value = state.precision;
                    unitConverter.currentPrecision = state.precision;
                    if (unitConverter.elements.precisionValue) {
                        unitConverter.elements.precisionValue.textContent = state.precision;
                    }
                }
                
                // 恢復主題 / Restore theme
                if (state.theme) {
                    unitConverter.setTheme(state.theme);
                }
                
                // 更新相關選項 / Update related options
                unitConverter.updateUnitOptions();
            }
        } catch (error) {
            console.error('恢復狀態失敗:', error);
        }
        
        // 報告初始化完成 / Report initialization complete
        console.log('🎉 單位轉換器應用完全載入完成');
        
        // 觸發自定義事件 / Trigger custom event
        window.dispatchEvent(new CustomEvent('unitConverterReady', {
            detail: { converter: unitConverter }
        }));
    }
});

/**
 * 錯誤監聽器 - 全域錯誤處理
 * Error listener - Global error handling
 */
window.addEventListener('error', (event) => {
    console.error('全域錯誤:', event.error);
    
    // 顯示用戶友好的錯誤信息 / Show user-friendly error message
    if (unitConverter && unitConverter.showToast) {
        unitConverter.showToast('發生未預期的錯誤,某些功能可能無法正常使用', 'error');
    }
});

/**
 * 未處理的 Promise 拒絕監聽 - 異步錯誤處理
 * Unhandled promise rejection listener - Async error handling
 */
window.addEventListener('unhandledrejection', (event) => {
    console.error('未處理的 Promise 拒絕:', event.reason);
    
    // 顯示錯誤提示 / Show error message
    if (unitConverter && unitConverter.showToast) {
        unitConverter.showToast('網路請求失敗,請檢查連接後重試', 'error');
    }
    
    // 防止錯誤在控制台中顯示 / Prevent error from showing in console
    event.preventDefault();
});

效能優化

計算效能優化

  • 防抖機制:300ms 防抖避免頻繁計算
  • 異步處理:避免阻塞主線程
  • 精度控制:根據需求調整計算精度
  • 快取機制:DOM 元素引用快取

記憶體管理

  • 歷史記錄限制:最多保存 50 條記錄
  • 事件委託:減少事件監聽器數量
  • 資源清理:頁面卸載時清理計時器
  • 本地存儲優化:定期清理過期數據

安全性考量

數據隱私保護

  • 本地處理:所有計算在瀏覽器端完成
  • 零數據上傳:不向任何服務器傳輸敏感數據
  • 本地存儲:僅使用 localStorage 保存偏好設置
  • 無追蹤:不包含任何分析或追蹤代碼

輸入驗證

  • 數值檢查:嚴格的數值有效性驗證
  • 範圍限制:防止極值計算溢出
  • 類型檢查:確保輸入數據類型正確
  • 錯誤處理:完善的異常處理機制

瀏覽器兼容性

瀏覽器 最低版本 支援狀態
Chrome 60+ ✅ 完全支援
Firefox 55+ ✅ 完全支援
Safari 12+ ✅ 完全支援
Edge 79+ ✅ 完全支援
IE 11 ⚠️ 部分支援

注意:本工具使用了 ES6+ 語法和現代瀏覽器 API,建議使用最新版本的瀏覽器以獲得最佳體驗。

未來改進方向

功能擴展

  • 更多單位類別:能量、功率、角度等
  • 自定義單位:允許用戶添加自定義單位
  • 批量轉換:支援批量數據轉換
  • 匯出功能:將結果匯出為 CSV 或 PDF

用戶體驗優化

  • 鍵盤快捷鍵:提供更多快捷鍵支援
  • 語音輸入:支援語音數值輸入
  • 公式顯示:顯示轉換公式和過程
  • 歷史搜索:歷史記錄搜索功能

總結

單位轉換器是一個功能完整、效能優化的純前端應用,採用現代 JavaScript 技術開發。 通過模組化設計、智能快取、防抖機制等技術確保了良好的用戶體驗。 完整的雙語支援和響應式設計使其適用於各種使用場景。 嚴格的隱私保護措施確保用戶數據安全,所有處理都在本地完成。

🎯 核心特色

  • ✅ 9大類別,50+ 種單位支援
  • ✅ 實時計算,300ms 防抖優化
  • ✅ 完整雙語系統,無縫切換
  • ✅ 智能歷史記錄,去重保存
  • ✅ 響應式設計,跨設備支援
  • ✅ 100% 隱私保護,本地處理