第02章 开发环境搭建与工具
12/3/25About 13 min
第 2 章:开发环境搭建与工具
学习目标
- 配置 Chrome Extension 开发环境
- 掌握开发者工具和调试技巧
- 了解常用的开发工具和框架
知识点总结
开发环境组成
Chrome Extension 开发环境主要包含以下几个部分:
- 浏览器环境: Chrome 浏览器(开发版或稳定版)
- 代码编辑器: VS Code、WebStorm 等
- 版本控制: Git
- 构建工具: Webpack、Vite、Parcel(可选)
- 开发框架: React、Vue、Angular(可选)
开发工具链架构
基础开发环境搭建
必要工具安装
"""
开发环境配置检查脚本
"""
import subprocess
import json
import os
class DevelopmentEnvironment:
"""开发环境配置类"""
def __init__(self):
self.tools = {
"chrome": False,
"node": False,
"npm": False,
"git": False,
"vscode": False
}
self.versions = {}
def check_tool(self, tool_name, command):
"""
检查工具是否安装
Args:
tool_name: 工具名称
command: 检查命令
"""
try:
# 模拟执行命令检查
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True
)
if result.returncode == 0:
self.tools[tool_name] = True
self.versions[tool_name] = result.stdout.strip()
return True
return False
except Exception as e:
print(f"检查 {tool_name} 时出错: {e}")
return False
def check_environment(self):
"""检查完整的开发环境"""
print("🔍 检查开发环境配置...\n")
# 检查各个工具
checks = [
("chrome", "google-chrome --version || chrome --version"),
("node", "node --version"),
("npm", "npm --version"),
("git", "git --version"),
("vscode", "code --version")
]
for tool, cmd in checks:
if self.check_tool(tool, cmd):
print(f"✅ {tool.upper()}: 已安装 (版本: {self.versions.get(tool, 'unknown')})")
else:
print(f"❌ {tool.upper()}: 未安装")
# 检查Chrome开发者模式
print("\n📋 Chrome 开发者模式设置:")
print("1. 打开 Chrome 浏览器")
print("2. 访问 chrome://extensions/")
print("3. 启用右上角的'开发者模式'开关")
return all(self.tools.values())
def setup_project_structure(self, project_name):
"""
设置项目基础结构
Args:
project_name: 项目名称
"""
structure = {
"src": {
"background": [],
"content": [],
"popup": [],
"options": [],
"assets": {
"icons": [],
"images": []
}
},
"dist": {},
"test": {},
"docs": {}
}
def create_dirs(base_path, structure_dict):
"""递归创建目录结构"""
for dir_name, sub_structure in structure_dict.items():
dir_path = os.path.join(base_path, dir_name)
os.makedirs(dir_path, exist_ok=True)
if isinstance(sub_structure, dict) and sub_structure:
create_dirs(dir_path, sub_structure)
# 创建项目目录
project_path = f"./{project_name}"
create_dirs(project_path, structure)
# 创建基础配置文件
self.create_config_files(project_path, project_name)
print(f"✅ 项目结构已创建: {project_path}")
return project_path
def create_config_files(self, project_path, project_name):
"""创建基础配置文件"""
# package.json
package_json = {
"name": project_name,
"version": "1.0.0",
"description": "Chrome Extension project",
"scripts": {
"build": "webpack --mode production",
"dev": "webpack --mode development --watch",
"test": "jest",
"lint": "eslint src/"
},
"devDependencies": {
"webpack": "^5.0.0",
"webpack-cli": "^4.0.0",
"copy-webpack-plugin": "^9.0.0",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^8.0.0",
"jest": "^27.0.0"
}
}
with open(f"{project_path}/package.json", "w") as f:
json.dump(package_json, f, indent=2)
# .gitignore
gitignore_content = """# Dependencies
node_modules/
# Build output
dist/
build/
*.zip
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
# Test coverage
coverage/
"""
with open(f"{project_path}/.gitignore", "w") as f:
f.write(gitignore_content)
# README.md
readme_content = f"""# {project_name}
Chrome Extension 项目
## 安装
```bash
npm install开发
npm run dev构建
npm run build测试
npm test"""
with open(f"{project_path}/README.md", "w") as f:
f.write(readme_content)
使用示例
env = DevelopmentEnvironment()
env.check_environment()
env.setup_project_structure("my-chrome-extension")
### VS Code 扩展配置
```python
"""
VS Code 开发环境优化配置
"""
class VSCodeSetup:
"""VS Code 配置类"""
def __init__(self, project_path):
self.project_path = project_path
self.vscode_dir = os.path.join(project_path, ".vscode")
def create_vscode_config(self):
"""创建 VS Code 配置文件"""
os.makedirs(self.vscode_dir, exist_ok=True)
# 推荐的扩展
extensions = {
"recommendations": [
"dbaeumer.vscode-eslint", # ESLint
"esbenp.prettier-vscode", # Prettier
"christian-kohler.path-intellisense", # 路径智能提示
"formulahendry.auto-rename-tag", # 自动重命名标签
"ritwickdey.liveserver", # Live Server
"ms-vscode.vscode-typescript-tslint-plugin", # TSLint
"octref.vetur", # Vue 支持
"dsznajder.es7-react-js-snippets", # React 代码片段
"msjsdiag.debugger-for-chrome", # Chrome 调试器
"naumovs.color-highlight" # 颜色高亮
]
}
with open(f"{self.vscode_dir}/extensions.json", "w") as f:
json.dump(extensions, f, indent=2)
# 工作区设置
settings = {
"editor.formatOnSave": True,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": True
},
"editor.tabSize": 2,
"editor.wordWrap": "on",
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"emmet.includeLanguages": {
"javascript": "javascriptreact"
},
"search.exclude": {
"**/node_modules": True,
"**/dist": True,
"**/build": True
}
}
with open(f"{self.vscode_dir}/settings.json", "w") as f:
json.dump(settings, f, indent=2)
# 代码片段
snippets = {
"Chrome Extension Manifest": {
"prefix": "manifest",
"body": [
"{",
" \"manifest_version\": 3,",
" \"name\": \"${1:Extension Name}\",",
" \"version\": \"${2:1.0.0}\",",
" \"description\": \"${3:Description}\",",
" \"permissions\": [${4}],",
" \"action\": {",
" \"default_popup\": \"popup.html\"",
" }",
"}"
],
"description": "Chrome Extension Manifest V3 template"
},
"Content Script": {
"prefix": "content",
"body": [
"// Content script for Chrome Extension",
"console.log('Content script loaded');",
"",
"// Interact with the page DOM",
"document.addEventListener('DOMContentLoaded', () => {",
" ${1:// Your code here}",
"});"
],
"description": "Content script template"
},
"Chrome Storage Get": {
"prefix": "chromeget",
"body": [
"chrome.storage.${1|local,sync|}.get(['${2:key}'], (result) => {",
" console.log('Value:', result.${2:key});",
" ${3:// Handle the result}",
"});"
],
"description": "Chrome storage get template"
}
}
with open(f"{self.vscode_dir}/chrome-extension.code-snippets", "w") as f:
json.dump(snippets, f, indent=2)
# 启动配置
launch_config = {
"version": "0.2.0",
"configurations": [
{
"name": "Launch Chrome Extension",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src",
"runtimeArgs": [
"--load-extension=${workspaceFolder}/dist"
]
}
]
}
with open(f"{self.vscode_dir}/launch.json", "w") as f:
json.dump(launch_config, f, indent=2)
print("✅ VS Code 配置文件已创建")
return self.vscode_dir
# 使用示例
vscode_setup = VSCodeSetup("./my-chrome-extension")
vscode_setup.create_vscode_config()开发者工具使用
Chrome DevTools 功能详解
"""
Chrome 开发者工具功能演示
"""
class ChromeDevTools:
"""Chrome 开发者工具类"""
def __init__(self):
self.panels = {
"Elements": "检查和修改DOM/CSS",
"Console": "JavaScript控制台",
"Sources": "调试JavaScript代码",
"Network": "监控网络请求",
"Performance": "性能分析",
"Memory": "内存分析",
"Application": "存储和缓存管理",
"Security": "安全性检查"
}
def extension_debugging_guide(self):
"""扩展程序调试指南"""
debugging_steps = {
"背景脚本调试": [
"打开 chrome://extensions/",
"找到你的扩展",
"点击'背景页'或'Service Worker'链接",
"DevTools 将打开,显示背景脚本控制台"
],
"弹窗页面调试": [
"右键点击扩展图标",
"选择'审查弹出内容'",
"DevTools 将打开,显示弹窗页面"
],
"内容脚本调试": [
"在目标网页上按 F12",
"转到 Sources 面板",
"在左侧找到 Content Scripts 部分",
"选择你的扩展脚本"
],
"选项页面调试": [
"打开扩展的选项页面",
"按 F12 打开 DevTools",
"像调试普通网页一样调试"
]
}
print("🔧 Chrome Extension 调试指南\n")
for area, steps in debugging_steps.items():
print(f"📌 {area}:")
for i, step in enumerate(steps, 1):
print(f" {i}. {step}")
print()
return debugging_steps
def console_utilities(self):
"""控制台实用命令"""
utilities = {
"$_": "返回最近一次表达式的值",
"$0-$4": "返回最近选择的DOM元素",
"$(selector)": "等同于 document.querySelector",
"$$(selector)": "等同于 document.querySelectorAll",
"clear()": "清空控制台",
"copy(object)": "复制对象到剪贴板",
"debug(function)": "在函数入口设置断点",
"monitor(function)": "监控函数调用",
"monitorEvents(element, events)": "监控元素事件",
"getEventListeners(element)": "获取元素的事件监听器"
}
print("💻 控制台实用命令:\n")
for cmd, desc in utilities.items():
print(f" {cmd:<30} - {desc}")
# 扩展专用命令
extension_commands = [
"chrome.runtime.getManifest()", # 获取manifest信息
"chrome.runtime.id", # 获取扩展ID
"chrome.management.getSelf(console.log)", # 获取扩展详情
"chrome.storage.local.get(null, console.log)", # 查看本地存储
"chrome.permissions.getAll(console.log)" # 查看权限
]
print("\n📦 扩展专用命令:")
for cmd in extension_commands:
print(f" {cmd}")
return utilities, extension_commands
def performance_profiling(self):
"""性能分析方法"""
profiling_tools = {
"Performance Panel": {
"purpose": "记录运行时性能",
"usage": [
"点击录制按钮",
"执行要分析的操作",
"停止录制",
"分析火焰图和时间轴"
],
"metrics": ["FPS", "CPU使用率", "网络活动", "内存"]
},
"Memory Panel": {
"purpose": "内存分析和泄漏检测",
"usage": [
"选择 Heap snapshot",
"点击 Take snapshot",
"比较不同时间点的快照",
"查找内存泄漏"
],
"metrics": ["堆内存大小", "对象数量", "内存分配"]
},
"Coverage Panel": {
"purpose": "代码覆盖率分析",
"usage": [
"打开 Coverage 面板",
"点击录制",
"刷新页面或执行操作",
"查看未使用的代码"
],
"metrics": ["JS覆盖率", "CSS覆盖率"]
}
}
for tool_name, tool_info in profiling_tools.items():
print(f"\n🎯 {tool_name}")
print(f" 目的: {tool_info['purpose']}")
print(" 使用步骤:")
for step in tool_info['usage']:
print(f" • {step}")
print(f" 关键指标: {', '.join(tool_info['metrics'])}")
return profiling_tools
# 使用示例
devtools = ChromeDevTools()
devtools.extension_debugging_guide()
devtools.console_utilities()
devtools.performance_profiling()调试技巧和最佳实践
"""
高级调试技巧和工具
"""
class AdvancedDebugging:
"""高级调试类"""
def __init__(self):
self.debugging_tips = []
def breakpoint_strategies(self):
"""断点策略"""
strategies = {
"条件断点": {
"description": "只在特定条件下暂停",
"example": "i === 5 || error !== null",
"usage": "右键点击行号 -> 添加条件断点"
},
"日志断点": {
"description": "不暂停,只输出日志",
"example": "console.log('value:', variable)",
"usage": "右键点击行号 -> 添加日志点"
},
"DOM断点": {
"description": "DOM变化时触发",
"types": ["子树修改", "属性修改", "节点删除"],
"usage": "Elements面板 -> 右键元素 -> Break on"
},
"XHR/Fetch断点": {
"description": "网络请求时触发",
"example": "URL包含特定字符串",
"usage": "Sources面板 -> XHR/fetch Breakpoints"
},
"事件监听器断点": {
"description": "特定事件触发时暂停",
"categories": ["鼠标", "键盘", "触摸", "控制"],
"usage": "Sources面板 -> Event Listener Breakpoints"
}
}
print("🎯 断点策略详解:\n")
for name, info in strategies.items():
print(f"📍 {name}")
for key, value in info.items():
if isinstance(value, list):
print(f" {key}: {', '.join(value)}")
else:
print(f" {key}: {value}")
print()
return strategies
def error_handling_patterns(self):
"""错误处理模式"""
patterns = """
// 1. 全局错误捕获
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
// 发送错误报告
reportError({
message: event.message,
source: event.filename,
line: event.lineno,
column: event.colno,
error: event.error.stack
});
});
// 2. Promise 错误捕获
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
event.preventDefault();
});
// 3. Chrome Extension 错误处理
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
try {
// 处理消息
handleMessage(request, sender, sendResponse);
} catch (error) {
console.error('Message handling error:', error);
sendResponse({success: false, error: error.message});
}
return true; // 保持消息通道开放
});
// 4. 异步错误处理
async function safeAsyncOperation() {
try {
const result = await riskyOperation();
return {success: true, data: result};
} catch (error) {
console.error('Async operation failed:', error);
return {success: false, error: error.message};
}
}
// 5. 开发环境调试辅助
const DEBUG = true; // 生产环境设为 false
function debugLog(...args) {
if (DEBUG) {
console.log('[DEBUG]', new Date().toISOString(), ...args);
}
}
// 6. 性能监控
function measurePerformance(functionName, fn) {
return function(...args) {
const startTime = performance.now();
const result = fn.apply(this, args);
const endTime = performance.now();
console.log(`${functionName} took ${endTime - startTime}ms`);
return result;
};
}
"""
print("🛡️ 错误处理模式示例:\n")
print(patterns)
return patterns
def remote_debugging_setup(self):
"""远程调试设置"""
setup_steps = {
"启动Chrome远程调试": {
"command": "chrome --remote-debugging-port=9222",
"purpose": "允许外部工具连接调试",
"use_cases": ["自动化测试", "远程开发", "CI/CD集成"]
},
"连接到远程调试": {
"url": "http://localhost:9222",
"tools": ["Chrome DevTools", "VS Code", "Puppeteer"],
"capabilities": ["检查页面", "执行脚本", "性能分析"]
},
"扩展程序自动重载": {
"tool": "Extension Reloader",
"benefit": "修改代码后自动重新加载扩展",
"setup": "安装Extension Reloader扩展"
}
}
print("🌐 远程调试配置:\n")
for feature, details in setup_steps.items():
print(f"📋 {feature}")
for key, value in details.items():
if isinstance(value, list):
print(f" {key}: {', '.join(value)}")
else:
print(f" {key}: {value}")
print()
return setup_steps
# 使用示例
debugging = AdvancedDebugging()
debugging.breakpoint_strategies()
debugging.error_handling_patterns()
debugging.remote_debugging_setup()常用开发工具和框架
构建工具配置
"""
构建工具配置示例
"""
class BuildToolsConfig:
"""构建工具配置类"""
def webpack_config(self):
"""Webpack 配置示例"""
config = """
// webpack.config.js
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: process.env.NODE_ENV || 'development',
devtool: 'inline-source-map',
entry: {
popup: './src/popup/popup.js',
options: './src/options/options.js',
background: './src/background/background.js',
content: './src/content/content.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]/[name].js'
},
module: {
rules: [
{
test: /\\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\\.(png|jpg|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/'
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: 'src/manifest.json' },
{ from: 'src/assets', to: 'assets' }
]
}),
new HtmlWebpackPlugin({
template: 'src/popup/popup.html',
filename: 'popup/popup.html',
chunks: ['popup']
}),
new HtmlWebpackPlugin({
template: 'src/options/options.html',
filename: 'options/options.html',
chunks: ['options']
})
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\\\/]node_modules[\\\\/]/,
name: 'vendor',
priority: 10
}
}
}
}
};
"""
print("📦 Webpack 配置示例:")
print(config)
return config
def vite_config(self):
"""Vite 配置示例"""
config = """
// vite.config.js
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';
import manifest from './src/manifest.json';
export default defineConfig({
plugins: [
crx({ manifest })
],
build: {
rollupOptions: {
input: {
popup: 'src/popup/index.html',
options: 'src/options/index.html'
},
output: {
entryFileNames: '[name]/[name].js',
chunkFileNames: 'chunks/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
}
}
},
server: {
port: 3000,
hmr: {
port: 3001
}
}
});
"""
print("\n⚡ Vite 配置示例:")
print(config)
return config
def npm_scripts(self):
"""NPM脚本配置"""
scripts = {
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"build:firefox": "cross-env TARGET=firefox webpack --mode production",
"clean": "rimraf dist",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/ --ext .js,.jsx",
"lint:fix": "eslint src/ --ext .js,.jsx --fix",
"format": "prettier --write 'src/**/*.{js,jsx,css,html}'",
"analyze": "webpack-bundle-analyzer dist/stats.json",
"zip": "node scripts/zip.js",
"release": "npm run clean && npm run build && npm run zip"
}
print("\n📜 NPM Scripts 配置:")
for name, command in scripts.items():
print(f" \"{name}\": \"{command}\"")
return scripts
# 使用示例
build_tools = BuildToolsConfig()
build_tools.webpack_config()
build_tools.vite_config()
build_tools.npm_scripts()框架集成示例
"""
主流框架集成Chrome Extension开发
"""
class FrameworkIntegration:
"""框架集成类"""
def react_setup(self):
"""React 集成配置"""
react_example = """
// React Popup 组件示例
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
function PopupApp() {
const [tabs, setTabs] = useState([]);
const [activeTab, setActiveTab] = useState(null);
useEffect(() => {
// 获取所有标签页
chrome.tabs.query({}, (tabs) => {
setTabs(tabs);
});
// 获取当前活动标签页
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
if (tabs[0]) {
setActiveTab(tabs[0]);
}
});
}, []);
const handleTabClick = (tabId) => {
chrome.tabs.update(tabId, {active: true});
};
return (
<div className="popup-container">
<h2>Tab Manager</h2>
<div className="current-tab">
{activeTab && (
<div>
<strong>当前标签:</strong>
<p>{activeTab.title}</p>
</div>
)}
</div>
<div className="tab-list">
<h3>所有标签页</h3>
{tabs.map(tab => (
<div
key={tab.id}
className="tab-item"
onClick={() => handleTabClick(tab.id)}
>
<img src={tab.favIconUrl} width="16" height="16" />
<span>{tab.title}</span>
</div>
))}
</div>
</div>
);
}
ReactDOM.render(<PopupApp />, document.getElementById('root'));
"""
print("⚛️ React 集成示例:")
print(react_example)
return react_example
def vue_setup(self):
"""Vue 集成配置"""
vue_example = """
<!-- Vue Popup 组件示例 -->
<template>
<div id="app">
<h2>{{ title }}</h2>
<div class="settings">
<label>
<input
type="checkbox"
v-model="darkMode"
@change="saveSetting"
>
启用深色模式
</label>
<label>
<input
type="checkbox"
v-model="notifications"
@change="saveSetting"
>
启用通知
</label>
</div>
<div class="actions">
<button @click="captureTab">截取当前页面</button>
<button @click="clearData">清除数据</button>
</div>
</div>
</template>
<script>
export default {
name: 'PopupApp',
data() {
return {
title: 'Extension Settings',
darkMode: false,
notifications: true
}
},
mounted() {
this.loadSettings();
},
methods: {
loadSettings() {
chrome.storage.sync.get(['darkMode', 'notifications'], (result) => {
this.darkMode = result.darkMode || false;
this.notifications = result.notifications !== false;
});
},
saveSetting() {
chrome.storage.sync.set({
darkMode: this.darkMode,
notifications: this.notifications
});
},
captureTab() {
chrome.tabs.captureVisibleTab((dataUrl) => {
// 处理截图
console.log('Screenshot captured');
});
},
clearData() {
if (confirm('确定要清除所有数据吗?')) {
chrome.storage.sync.clear(() => {
console.log('Data cleared');
this.loadSettings();
});
}
}
}
}
</script>
"""
print("\n🟢 Vue 集成示例:")
print(vue_example)
return vue_example
def typescript_setup(self):
"""TypeScript 配置"""
ts_config = """
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"types": ["chrome", "node"],
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
// Chrome API 类型定义示例
interface ExtensionMessage {
type: string;
payload: any;
}
interface TabInfo {
id: number;
url: string;
title: string;
active: boolean;
}
// 类型安全的消息处理
chrome.runtime.onMessage.addListener(
(message: ExtensionMessage, sender: chrome.runtime.MessageSender,
sendResponse: (response?: any) => void) => {
switch(message.type) {
case 'GET_TAB_INFO':
const tabInfo: TabInfo = {
id: sender.tab?.id || 0,
url: sender.tab?.url || '',
title: sender.tab?.title || '',
active: sender.tab?.active || false
};
sendResponse(tabInfo);
break;
}
return true;
}
);
"""
print("\n📘 TypeScript 配置示例:")
print(ts_config)
return ts_config
# 使用示例
framework = FrameworkIntegration()
framework.react_setup()
framework.vue_setup()
framework.typescript_setup()开发工作流优化
自动化工作流
"""
开发工作流自动化配置
"""
class WorkflowAutomation:
"""工作流自动化类"""
def hot_reload_setup(self):
"""热重载配置"""
hot_reload_script = """
// hot-reload.js - 开发环境自动重载脚本
const filesInDirectory = dir => new Promise(resolve =>
dir.createReader().readEntries(entries =>
Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
e.isDirectory
? filesInDirectory(e)
: new Promise(resolve => e.file(resolve))
))
.then(files => [].concat(...files))
.then(resolve)
)
);
const timestampForFilesInDirectory = dir =>
filesInDirectory(dir).then(files =>
files.map(f => f.name + f.lastModified).join()
);
const watchChanges = (dir, lastTimestamp) => {
timestampForFilesInDirectory(dir).then(timestamp => {
if (!lastTimestamp || (lastTimestamp === timestamp)) {
setTimeout(() => watchChanges(dir, timestamp), 1000);
} else {
chrome.runtime.reload();
}
});
};
// 仅在开发环境启用
if (process.env.NODE_ENV === 'development') {
chrome.management.getSelf(self => {
if (self.installType === 'development') {
chrome.runtime.getPackageDirectoryEntry(dir =>
watchChanges(dir)
);
}
});
}
"""
print("🔄 热重载脚本:")
print(hot_reload_script)
return hot_reload_script
def git_hooks_setup(self):
"""Git Hooks 配置"""
hooks = {
"pre-commit": """#!/bin/sh
# 运行 lint 检查
npm run lint
# 运行测试
npm test
# 检查是否有console.log
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.(js|jsx|ts|tsx)$')
if [ -n "$FILES" ]; then
echo "$FILES" | xargs grep -n "console\\.log" && {
echo "❌ 发现 console.log,请移除后再提交"
exit 1
}
fi
exit 0
""",
"pre-push": """#!/bin/sh
# 构建项目
npm run build
# 运行完整测试套件
npm run test:full
exit 0
""",
"commit-msg": """#!/bin/sh
# 验证提交消息格式
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\\(.+\\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
echo "❌ 提交消息格式错误!"
echo "格式: <type>(<scope>): <subject>"
echo "示例: feat(popup): add new feature"
exit 1
fi
"""
}
print("🪝 Git Hooks 配置:\n")
for hook_name, hook_content in hooks.items():
print(f"📄 .git/hooks/{hook_name}")
print(hook_content)
print()
return hooks
def ci_cd_pipeline(self):
"""CI/CD 管道配置"""
github_actions = """
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Build extension
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: extension-build
path: dist/
release:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build for production
run: npm run build:prod
- name: Create release package
run: npm run package
- name: Upload to Chrome Web Store
env:
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
run: |
npx chrome-webstore-upload-cli upload \\
--source extension.zip \\
--extension-id ${{ secrets.EXTENSION_ID }}
"""
print("🚀 CI/CD Pipeline 配置:")
print(github_actions)
return github_actions
# 使用示例
workflow = WorkflowAutomation()
workflow.hot_reload_setup()
workflow.git_hooks_setup()
workflow.ci_cd_pipeline()开发效率提升建议
- 使用代码片段: 创建常用代码模板,快速生成样板代码
- 配置别名: 设置路径别名,简化导入语句
- 启用热重载: 自动重新加载扩展,无需手动刷新
- 使用调试工具: 充分利用Chrome DevTools的所有功能
- 版本控制: 使用Git进行版本管理,定期提交代码
本章小结
本章详细介绍了Chrome Extension开发环境的搭建和工具使用:
- 基础环境搭建: 配置了开发所需的基本工具和项目结构
- VS Code配置: 设置了适合扩展开发的编辑器环境
- 调试工具使用: 掌握了Chrome DevTools的各种调试技巧
- 构建工具配置: 学习了Webpack和Vite的配置方法
- 框架集成: 了解了React、Vue和TypeScript的集成方式
- 工作流优化: 配置了自动化工具提升开发效率
下一章将深入学习Manifest文件的详细配置。
