2026-02-09 15:52:45 +08:00

6.1 KiB
Raw Blame History

沙拉查词项目规范

架构原则、代码风格、通信协议、安全规范


1. 架构原则

模块职责

模块 职责 禁止行为
Background 网络请求、数据持久化、右键菜单 操作DOM
Content Script 读取页面文本、注入UI、用户交互 直接跨域请求
Popup 独立查词、快速设置、历史记录 访问页面DOM
Options 配置管理、生词本、词典账号 操作页面内容
Shared 工具函数、常量、基类 依赖特定模块

通信规则

Content ↔ Background: chrome.runtime.sendMessage
Popup ↔ Background: chrome.runtime.sendMessage
Options → Background: chrome.runtime.sendMessage

2. 代码风格

命名规范

类型 规范 示例
文件 小写+连字符 salad-icon.js
PascalCase DictionaryBase
函数 camelCase+动词开头 getSelectionCoords()
常量 UPPER_SNAKE_CASE DEFAULT_TIMEOUT
私有 下划线前缀 _handleMessage()
消息类型 大写+点分隔 DICT.SEARCH

JavaScript 规范

必须使用:

  • const / let,禁止 var
  • 箭头函数作为回调
  • 模板字符串
  • 解构赋值
  • 可选链 ?.

禁止:

  • eval(), new Function()
  • innerHTML 插入不可信内容
  • 同步的 chrome.storage 调用

异步规范:

// ✅ async/await
async function searchWord(word) {
  try {
    const result = await dict.search(word);
    return result;
  } catch (error) {
    console.error('Search failed:', error);
    throw error;
  }
}

CSS 规范

Shadow DOM 隔离:

const shadow = element.attachShadow({ mode: 'open' });
shadow.innerHTML = `
  <style>
    :host { display: block; }
    .container { background: var(--salad-bg, #fff); }
  </style>
  <div class="container">...</div>
`;

CSS 变量:

:root {
  --salad-primary: #4CAF50;
  --salad-bg: #ffffff;
  --salad-text: #333333;
}
[data-theme="dark"] {
  --salad-bg: #1a1a1a;
  --salad-text: #e0e0e0;
}

3. 通信协议

消息格式

// 请求
{
  type: 'NAMESPACE.ACTION',
  payload: { },
  meta: { timestamp, requestId }
}

// 响应
{
  type: 'NAMESPACE.ACTION_RESPONSE',
  payload: { },
  error: null,
  meta: { requestId }
}

标准消息类型

DICT.SEARCH / DICT.SEARCH_RESPONSE
CONFIG.GET / CONFIG.SET / CONFIG.CHANGED
WORD.ADD_FAVORITE / WORD.REMOVE_FAVORITE
CLIPBOARD.READ / CLIPBOARD.WRITE
HTTP.GET / HTTP.POST

通信封装

// shared/messaging.js
class MessageClient {
  async send(type, payload, timeout = 5000) {
    return new Promise((resolve, reject) => {
      // 实现:发送消息,监听响应,超时处理
    });
  }
}
export const messaging = new MessageClient();

4. 词典开发规范

基类定义

export class DictionaryBase {
  constructor(config = {}) {
    this.name = config.name || 'Unknown';
    this.icon = config.icon || '';
    this.languages = config.languages || ['en', 'zh'];
  }

  async search(word) {
    throw new Error('search() must be implemented');
  }

  supports(lang) {
    return this.languages.includes(lang);
  }
}

返回格式

{
  word: 'hello',
  phonetic: '/həˈləʊ/',
  meanings: [
    { partOfSpeech: 'int.', definitions: ['你好', '喂'] }
  ],
  examples: [
    { sentence: 'Hello, world!', translation: '你好,世界!' }
  ],
  url: 'https://...'
}

实现模板

export class XxxDictionary extends DictionaryBase {
  constructor(config = {}) {
    super({ name: '词典名', icon: 'icon.png', languages: ['en', 'zh'], ...config });
  }

  async search(word) {
    if (!word?.trim()) throw new Error('Word is empty');
    
    const html = await messaging.send('HTTP.GET', {
      url: `https://api.example.com?q=${encodeURIComponent(word)}`
    });
    
    return this.parse(html, word);
  }

  parse(html, word) {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    return {
      word,
      phonetic: this.extractPhonetic(doc),
      meanings: this.extractMeanings(doc),
      examples: this.extractExamples(doc),
      url: ''
    };
  }
}

5. UI 组件规范

组件基类

export class ComponentBase extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
    this._isVisible = false;
  }

  render() {
    throw new Error('render() must be implemented');
  }

  show(x, y) {
    this._isVisible = true;
    this.style.display = 'block';
    this.style.left = `${x}px`;
    this.style.top = `${y}px`;
  }

  hide() {
    this._isVisible = false;
    this.style.display = 'none';
  }

  destroy() {
    this.hide();
    this.remove();
  }
}

6. 存储规范

存储分层

chrome.storage.local    // 配置、生词本
chrome.storage.session  // 临时数据
IndexedDB              // 大数据量
Memory                 // 运行时缓存

配置结构

const DEFAULT_CONFIG = {
  version: '7.20.0',
  general: {
    enableSelection: true,
    enableAnimation: true,
    darkMode: false,
    language: 'zh_CN'
  },
  searchMode: {
    triggerMode: 'icon',  // icon/direct/double/key
    iconHoverSearch: false,
    shortcutKeys: []
  },
  dictionaries: [
    { id: 'bing', enabled: true, order: 0 }
  ]
};

7. 安全规范

XSS 防护

// ✅ textContent
element.textContent = userInput;

// ❌ innerHTML 插入不可信内容
element.innerHTML = userInput;

// ✅ 必须使用时转义
function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

CSP

{
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  }
}

8. 版本号规范

遵循 SemVerPATCH 与验收点关联:

MAJOR.MINOR.PATCH
│     │     └─ 每个任务完成 +1
│     └─ 模块完成进入下一模块 +1  
└─ 正式发布 = 1

详见 README.md 版本规则部分和 VERSION.md