Skip to content

JavaScript 编码规范

概述

本文档定义了 Eagle2Ae After Effects CEP 扩展的 JavaScript 编码规范和最佳实践,确保代码质量、可维护性和团队协作效率。

基础语法规范

缩进和空格

javascript
// 使用 4 个空格缩进
function processFiles(files) {
    if (files && files.length > 0) {
        files.forEach(file => {
            console.log(`处理文件: ${file.name}`);
        });
    }
}

// 操作符前后添加空格
const result = a + b * c;
const isValid = (value !== null) && (value !== undefined);

// 对象和数组的格式
const config = {
    server: {
        url: 'ws://localhost:8080',
        timeout: 5000
    },
    import: {
        mode: 'footage',
        createComposition: true
    }
};

const supportedFormats = [
    'jpg', 'jpeg', 'png', 'tiff',
    'mp4', 'mov', 'avi',
    'wav', 'mp3', 'aiff'
];

分号和引号

javascript
// 必须使用分号
const message = 'Hello World';
const result = calculateSum(a, b);

// 字符串优先使用单引号
const singleQuote = 'This is preferred';
const doubleQuote = "Only when containing 'single quotes'";

// JSON 数据使用双引号
const jsonData = {
    "type": "file_transfer",
    "data": {
        "files": []
    }
};

命名规范

变量和函数命名

javascript
// 变量使用 camelCase
const websocketClient = new WebSocketClient();
const fileImportManager = new FileImportManager();
const currentProjectInfo = getProjectInfo();

// 常量使用 UPPER_SNAKE_CASE
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
const DEFAULT_TIMEOUT = 5000;
const SUPPORTED_IMAGE_FORMATS = ['jpg', 'jpeg', 'png', 'tiff'];

// 函数使用 camelCase,动词开头
function connectToServer() { /* ... */ }
function validateFileFormat(extension) { /* ... */ }
function createCompositionFromFiles(files) { /* ... */ }

// 布尔值使用 is/has/can 前缀
const isConnected = client.connected;
const hasValidFiles = files.every(file => file.valid);
const canImportFile = checkFilePermissions(filePath);

// 私有方法使用下划线前缀
class WebSocketClient {
    connect() {
        this._initializeConnection();
    }
    
    _initializeConnection() {
        // 私有方法实现
    }
}

类和构造函数命名

javascript
// 类名使用 PascalCase
class WebSocketClient {
    constructor(url, options = {}) {
        this.url = url;
        this.options = options;
    }
}

class FileImportManager {
    constructor(aeInterface) {
        this.aeInterface = aeInterface;
    }
}

// 工厂函数使用 camelCase
function createWebSocketClient(url, options) {
    return new WebSocketClient(url, options);
}

注释规范

文件头注释

javascript
/**
 * WebSocket 客户端管理器
 * 负责与 Eagle 插件的 WebSocket 通信
 * 
 * @author Eagle2Ae 开发团队
 * @date 2024-01-05
 * @version 1.0.0
 * @since 1.0.0
 */

函数注释

javascript
/**
 * 建立 WebSocket 连接
 * @param {string} url - WebSocket 服务器地址
 * @param {Object} options - 连接选项
 * @param {number} options.timeout - 连接超时时间(毫秒)
 * @param {number} options.retryCount - 重试次数
 * @param {boolean} options.autoReconnect - 是否自动重连
 * @returns {Promise<WebSocket>} WebSocket 连接实例
 * @throws {Error} 连接失败时抛出错误
 * @example
 * const client = await connectWebSocket('ws://localhost:8080', {
 *     timeout: 5000,
 *     retryCount: 3,
 *     autoReconnect: true
 * });
 */
async function connectWebSocket(url, options = {}) {
    // 实现代码...
}

类注释

javascript
/**
 * 文件导入管理器
 * 负责处理文件导入到 After Effects 的所有操作
 * 
 * @class FileImportManager
 * @example
 * const manager = new FileImportManager(csInterface);
 * const result = await manager.importFiles(files, settings);
 */
class FileImportManager {
    /**
     * 构造函数
     * @param {Object} csInterface - CEP CSInterface 实例
     */
    constructor(csInterface) {
        this.csInterface = csInterface;
    }
    
    /**
     * 导入文件到 After Effects
     * @param {Array<Object>} files - 文件信息数组
     * @param {Object} settings - 导入设置
     * @returns {Promise<Object>} 导入结果
     */
    async importFiles(files, settings) {
        // 实现代码...
    }
}

行内注释

javascript
function processFileImport(files, settings) {
    // 验证文件列表
    if (!files || files.length === 0) {
        throw new Error('文件列表不能为空');
    }
    
    // 过滤支持的文件格式
    const validFiles = files.filter(file => {
        const extension = getFileExtension(file.path);
        return SUPPORTED_FORMATS.includes(extension.toLowerCase());
    });
    
    // 如果没有有效文件,提前返回
    if (validFiles.length === 0) {
        return {
            success: false,
            error: '没有找到支持的文件格式'
        };
    }
    
    // 开始批量导入处理
    return batchImportFiles(validFiles, settings);
}

错误处理规范

异步错误处理

javascript
// 使用 try-catch 处理异步操作
async function importFileToAE(filePath, options) {
    try {
        // 验证文件路径
        const isValid = await validateFilePath(filePath);
        if (!isValid) {
            throw new Error(`无效的文件路径: ${filePath}`);
        }
        
        // 执行导入操作
        const result = await executeImport(filePath, options);
        
        // 记录成功日志
        logger.info('文件导入成功', {
            filePath,
            itemId: result.itemId,
            duration: result.duration
        });
        
        return result;
        
    } catch (error) {
        // 记录错误日志
        logger.error('文件导入失败', {
            filePath,
            error: error.message,
            stack: error.stack
        });
        
        // 重新抛出错误,让上层处理
        throw new Error(`文件导入失败: ${error.message}`);
    }
}

错误分类和处理

javascript
// 定义错误类型
class FileImportError extends Error {
    constructor(message, code, filePath) {
        super(message);
        this.name = 'FileImportError';
        this.code = code;
        this.filePath = filePath;
    }
}

class ConnectionError extends Error {
    constructor(message, url) {
        super(message);
        this.name = 'ConnectionError';
        this.url = url;
    }
}

// 使用自定义错误
function validateFile(filePath) {
    if (!fs.existsSync(filePath)) {
        throw new FileImportError(
            '文件不存在',
            'FILE_NOT_FOUND',
            filePath
        );
    }
    
    const extension = getFileExtension(filePath);
    if (!SUPPORTED_FORMATS.includes(extension)) {
        throw new FileImportError(
            '不支持的文件格式',
            'UNSUPPORTED_FORMAT',
            filePath
        );
    }
}

用户友好的错误提示

javascript
// 错误消息映射
const ERROR_MESSAGES = {
    'FILE_NOT_FOUND': '文件不存在,请检查文件路径是否正确',
    'UNSUPPORTED_FORMAT': '不支持的文件格式,请选择支持的图片或视频文件',
    'CONNECTION_FAILED': '无法连接到 Eagle 插件,请确保 Eagle 正在运行',
    'IMPORT_FAILED': '文件导入失败,请重试或检查 After Effects 状态',
    'INSUFFICIENT_MEMORY': '内存不足,请关闭其他应用程序后重试'
};

// 显示用户友好的错误信息
function showUserError(error) {
    const userMessage = ERROR_MESSAGES[error.code] || error.message;
    
    // 显示错误对话框
    showErrorDialog({
        title: '操作失败',
        message: userMessage,
        details: error.code ? `错误代码: ${error.code}` : null,
        buttons: ['确定', '重试']
    });
}

性能优化规范

异步操作优化

javascript
// 使用 Promise.all 并行处理
async function validateMultipleFiles(filePaths) {
    const validationPromises = filePaths.map(async (filePath) => {
        try {
            const isValid = await validateFile(filePath);
            return { filePath, valid: isValid };
        } catch (error) {
            return { filePath, valid: false, error: error.message };
        }
    });
    
    return Promise.all(validationPromises);
}

// 批量处理大量数据
async function batchProcessFiles(files, batchSize = 10) {
    const results = [];
    
    for (let i = 0; i < files.length; i += batchSize) {
        const batch = files.slice(i, i + batchSize);
        const batchResults = await Promise.all(
            batch.map(file => processFile(file))
        );
        results.push(...batchResults);
        
        // 在批次之间添加短暂延迟,避免阻塞 UI
        if (i + batchSize < files.length) {
            await new Promise(resolve => setTimeout(resolve, 10));
        }
    }
    
    return results;
}

内存管理

javascript
// 及时清理资源
class WebSocketClient {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.messageHandlers = new Map();
        this.heartbeatTimer = null;
    }
    
    connect() {
        // 连接逻辑...
    }
    
    disconnect() {
        // 清理定时器
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
        
        // 清理事件监听器
        this.messageHandlers.clear();
        
        // 关闭 WebSocket 连接
        if (this.ws) {
            this.ws.close();
            this.ws = null;
        }
    }
}

// 使用 WeakMap 避免内存泄漏
const fileCache = new WeakMap();

function cacheFileInfo(file, info) {
    fileCache.set(file, info);
}

function getCachedFileInfo(file) {
    return fileCache.get(file);
}

更新记录

日期版本更新内容作者
2024-01-051.0初始 JavaScript 编码规范文档开发团队

相关文档:

Released under the MIT License.