- 验证扩展可在 Chrome 中正常加载 - 修复 Vite HMR WebSocket 在 Chrome 扩展中的报错 - 禁用 HMR 避免 chrome-extension:// 协议限制 - 所有模块 Console 输出正常
218 lines
4.8 KiB
Markdown
218 lines
4.8 KiB
Markdown
# 快速参考
|
||
|
||
> 版本速查、命名规范、代码模板
|
||
|
||
---
|
||
|
||
## 版本速查
|
||
|
||
### 当前版本
|
||
`0.0.5` → 下一目标 `0.1.1` ([M2.1](./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.json(0.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.json(0.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)
|