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

320 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 沙拉查词项目规范
> 架构原则、代码风格、通信协议、安全规范
---
## 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` 调用
**异步规范**:
```javascript
// ✅ 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 隔离**:
```javascript
const shadow = element.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host { display: block; }
.container { background: var(--salad-bg, #fff); }
</style>
<div class="container">...</div>
`;
```
**CSS 变量**:
```css
:root {
--salad-primary: #4CAF50;
--salad-bg: #ffffff;
--salad-text: #333333;
}
[data-theme="dark"] {
--salad-bg: #1a1a1a;
--salad-text: #e0e0e0;
}
```
---
## 3. 通信协议
### 消息格式
```javascript
// 请求
{
type: 'NAMESPACE.ACTION',
payload: { },
meta: { timestamp, requestId }
}
// 响应
{
type: 'NAMESPACE.ACTION_RESPONSE',
payload: { },
error: null,
meta: { requestId }
}
```
### 标准消息类型
```javascript
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
```
### 通信封装
```javascript
// shared/messaging.js
class MessageClient {
async send(type, payload, timeout = 5000) {
return new Promise((resolve, reject) => {
// 实现:发送消息,监听响应,超时处理
});
}
}
export const messaging = new MessageClient();
```
---
## 4. 词典开发规范
### 基类定义
```javascript
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);
}
}
```
### 返回格式
```javascript
{
word: 'hello',
phonetic: '/həˈləʊ/',
meanings: [
{ partOfSpeech: 'int.', definitions: ['你好', '喂'] }
],
examples: [
{ sentence: 'Hello, world!', translation: '你好,世界!' }
],
url: 'https://...'
}
```
### 实现模板
```javascript
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 组件规范
### 组件基类
```javascript
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. 存储规范
### 存储分层
```javascript
chrome.storage.local // 配置、生词本
chrome.storage.session // 临时数据
IndexedDB // 大数据量
Memory // 运行时缓存
```
### 配置结构
```javascript
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 防护
```javascript
// ✅ textContent
element.textContent = userInput;
// ❌ innerHTML 插入不可信内容
element.innerHTML = userInput;
// ✅ 必须使用时转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
```
### CSP
```json
{
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}
```
---
## 8. 版本号规范
遵循 SemVer**PATCH 与验收点关联**:
```
MAJOR.MINOR.PATCH
│ │ └─ 每个任务完成 +1
│ └─ 模块完成进入下一模块 +1
└─ 正式发布 = 1
```
详见 [README.md](./README.md) 版本规则部分和 [VERSION.md](./VERSION.md)