李岩岩 4e12552437 feat(M2.5): 图标点击事件 (v0.1.5)
- SaladIcon 支持传入 onClick 回调函数
- 点击图标打印 'icon clicked' 到控制台
- 点击后图标保持显示
2026-02-10 16:18:15 +08:00

218 lines
4.8 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.

# 快速参考
> 版本速查、命名规范、代码模板
---
## 版本速查
### 当前版本
`0.1.5` → 下一目标 `0.1.6` ([M2.6](./M2.md))
### 模块版本范围
| 模块 | 范围 | 任务数 |
|------|------|--------|
| M1 | 0.0.1 ~ 0.0.5 | 5 |
| M2 | 0.1.1 ~ 0.1.9 | 9 |
| M3 | 0.2.1 ~ 0.2.11 | 11 |
| M4 | 0.3.1 ~ 0.3.8 | 8 |
| M5 | 0.4.1 ~ 0.4.16 | 16 |
| M6 | 0.5.1 ~ 0.5.7 | 7 |
| M7 | 0.6.1 ~ 0.6.6 | 6 |
| M8 | 0.7.1 ~ 0.7.10 | 10 |
| M9 | 0.8.1 ~ 0.8.7 | 7 |
| M10 | 0.9.1 ~ 0.9.8 | 8 |
| M11 | 0.10.1 ~ 1.0.0 | 10 |
### 版本更新
```bash
# M1.1 完成后
版本: 0.0.0 → 0.0.1
git commit -m "feat(M1.1): 项目初始化 (v0.0.1)"
# M2.1 完成后 (进入M2)
版本: 0.0.5 → 0.1.1
git commit -m "feat(M2.1): 文本选择检测 (v0.1.1)"
```
---
## 命名速查
| 类型 | 规范 | 示例 |
|------|------|------|
| 文件 | 小写+连字符 | `salad-icon.js` |
| 类 | PascalCase | `DictionaryBase` |
| 函数 | camelCase+动词 | `getSelectionCoords()` |
| 常量 | UPPER_SNAKE | `DEFAULT_TIMEOUT` |
| 私有 | _前缀 | `_cache`, `_handle()` |
| 消息 | 大写+点 | `DICT.SEARCH` |
---
## 消息类型速查
```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
```
---
## 代码模板
### package.json0.0.0
```json
{
"name": "salad-dict",
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.2.0",
"pinia": "^2.1.0",
"localforage": "^1.10.0"
},
"devDependencies": {
"@crxjs/vite-plugin": "^2.0.0",
"@vitejs/plugin-vue": "^5.0.0",
"vite": "^5.0.0"
}
}
```
### manifest.json0.0.0
```json
{
"manifest_version": 3,
"name": "沙拉查词",
"version": "0.0.0",
"permissions": ["storage", "contextMenus", "clipboardWrite"],
"optional_permissions": ["clipboardRead"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "src/background/index.js",
"type": "module"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["src/content/index.js"],
"run_at": "document_end"
}],
"action": {
"default_popup": "src/popup/index.html"
},
"options_page": "src/options/index.html"
}
```
### 词典实现
```javascript
import { DictionaryBase } from './base.js';
import { messaging } from '../messaging.js';
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: `...${encodeURIComponent(word)}` });
return this.parse(html, word);
}
parse(html, word) {
const doc = new DOMParser().parseFromString(html, 'text/html');
return {
word,
phonetic: '',
meanings: [{ partOfSpeech: 'n.', definitions: ['释义'] }],
examples: [{ sentence: '例句', translation: '翻译' }],
url: ''
};
}
}
```
### UI组件
```javascript
export class XxxComponent extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'open' });
this.render();
}
render() {
this.shadow.innerHTML = `
<style>
:host { display: block; position: fixed; }
.container { background: var(--salad-bg, #fff); }
</style>
<div class="container">...</div>
`;
}
show(x, y) {
this.style.left = `${x}px`;
this.style.top = `${y}px`;
this.style.display = 'block';
}
hide() {
this.style.display = 'none';
}
}
```
### 通信封装
```javascript
class MessageClient {
async send(type, payload, timeout = 5000) {
const requestId = generateUUID();
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('Timeout')), timeout);
const handler = (response) => {
if (response.meta?.requestId !== requestId) return;
clearTimeout(timer);
chrome.runtime.onMessage.removeListener(handler);
response.error ? reject(new Error(response.error.message)) : resolve(response.payload);
};
chrome.runtime.onMessage.addListener(handler);
chrome.runtime.sendMessage({ type, payload, meta: { requestId } });
});
}
}
export const messaging = new MessageClient();
```
---
## 调试技巧
```javascript
// Content Script
console.log('[Content]', message);
// Background (看 Service Worker console)
chrome.action.onClicked.addListener(() => {
console.log('[Background]', 'clicked');
});
// 强制刷新扩展
chrome.runtime.reload();
```
**Chrome 扩展 DevTools**:
- `chrome://extensions/` → "service worker" (background)
- "inspect views" (popup)