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

6.1 KiB
Raw Permalink 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