VSCode 插件开发实践
调试、测试与发布
目录
开发环境
创建项目
bash
# 使用 Yeoman 生成器
npm install -g yo generator-code
yo code
# 选择选项
? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? my-extension
? What's the identifier of your extension? my-extension
? What's the description? My first extension
? Enable strict TypeScript checking? Yes
? Setup linting using ESLint? Yes
? Initialize a git repository? Yes
? Which package manager to use? npm调试配置
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: watch"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test"
],
"outFiles": ["${workspaceFolder}/out/**/*.js"]
}
]
}调试技巧
Console 输出
typescript
// 输出通道(推荐)
const outputChannel = vscode.window.createOutputChannel('My Extension');
outputChannel.appendLine('Debug info');
outputChannel.show();
// 开发者工具 Console
console.log('Debug message');
// 状态栏临时信息
vscode.window.setStatusBarMessage('Processing...', 2000);断点调试
typescript
// 1. 在源码中设置断点
// 2. 按 F5 启动调试
// 3. 在 Extension Development Host 中触发命令
// 4. 断点命中后可查看变量、调用栈常见问题排查
typescript
// 检查激活
export function activate(context: vscode.ExtensionContext) {
console.log('Extension activated');
try {
// 初始化代码
} catch (error) {
console.error('Activation failed:', error);
vscode.window.showErrorMessage(`Activation failed: ${error}`);
}
}
// 检查命令注册
const disposable = vscode.commands.registerCommand('myCommand', () => {
console.log('Command executed');
});
if (!disposable) {
console.error('Command registration failed');
}测试策略
单元测试
typescript
// src/utils.ts
export function formatText(text: string): string {
return text.trim().toLowerCase();
}
// test/utils.test.ts
import * as assert from 'assert';
import { formatText } from '../src/utils';
suite('Utils Test Suite', () => {
test('formatText should trim and lowercase', () => {
assert.strictEqual(formatText(' Hello World '), 'hello world');
});
});集成测试
typescript
// test/extension.test.ts
import * as vscode from 'vscode';
import * as assert from 'assert';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Extension should be present', () => {
assert.ok(vscode.extensions.getExtension('publisher.my-extension'));
});
test('Command should be registered', async () => {
const commands = await vscode.commands.getCommands(true);
assert.ok(commands.includes('myExtension.helloWorld'));
});
test('Command should execute', async () => {
await vscode.commands.executeCommand('myExtension.helloWorld');
// 验证结果
});
});测试运行器配置
typescript
// test/runTest.ts
import * as path from 'path';
import { runTests } from '@vscode/test-electron';
async function main() {
try {
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
const extensionTestsPath = path.resolve(__dirname, './suite/index');
await runTests({
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: ['--disable-extensions']
});
} catch (err) {
console.error('Failed to run tests');
process.exit(1);
}
}
main();打包发布
安装 vsce
bash
npm install -g @vscode/vsce打包
bash
# 生成 .vsix 文件
vsce package
# 指定版本
vsce package --pre-release发布到 Marketplace
bash
# 1. 创建 Azure DevOps 组织
# 2. 获取 Personal Access Token (PAT)
# 3. 创建 publisher
vsce create-publisher <publisher-name>
# 4. 登录
vsce login <publisher-name>
# 5. 发布
vsce publish
# 或指定版本
vsce publish minor # 1.0.0 -> 1.1.0
vsce publish major # 1.0.0 -> 2.0.0
vsce publish patch # 1.0.0 -> 1.0.1package.json 检查清单
json
{
"name": "my-extension",
"displayName": "My Extension",
"description": "Description here",
"version": "1.0.0",
"publisher": "your-publisher-name",
"repository": {
"type": "git",
"url": "https://github.com/user/repo"
},
"icon": "images/icon.png",
"categories": ["Other"],
"keywords": ["keyword1", "keyword2"],
"engines": {
"vscode": "^1.80.0"
},
"license": "MIT"
}CI/CD 配置
yaml
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run test
- name: Publish
run: npx vsce publish -p ${{ secrets.VSCE_PAT }}性能优化
延迟加载
typescript
// 按需导入
export async function activate(context: vscode.ExtensionContext) {
// 延迟加载大型模块
const command = vscode.commands.registerCommand('myExtension.analyze', async () => {
const analyzer = await import('./analyzer');
analyzer.run();
});
context.subscriptions.push(command);
}减少激活事件
json
{
"activationEvents": [
// ❌ 避免使用 *
"*",
// ✅ 精确触发
"onCommand:myExtension.start",
"onLanguage:javascript"
]
}缓存策略
typescript
class DataCache {
private cache = new Map<string, { data: any; timestamp: number }>();
private ttl = 5 * 60 * 1000; // 5 分钟
get(key: string): any | undefined {
const entry = this.cache.get(key);
if (!entry) return undefined;
if (Date.now() - entry.timestamp > this.ttl) {
this.cache.delete(key);
return undefined;
}
return entry.data;
}
set(key: string, data: any): void {
this.cache.set(key, { data, timestamp: Date.now() });
}
}高频面试题
Q1: 如何调试 Extension Host 崩溃?
- 查看日志:Help → Toggle Developer Tools → Console
- 输出面板:View → Output → Extension Host
- 禁用其他插件:
--disable-extensions排除干扰
Q2: 如何减少插件体积?
- 排除不必要文件:
.vscodeignore - 生产构建:使用 esbuild/webpack 打包
- 依赖分析:移除未使用的 npm 包
# .vscodeignore
.vscode/**
src/**
node_modules/**
*.ts
tsconfig.jsonQ3: 发布前的检查项?
- [ ]
publisher已配置 - [ ]
repository已填写 - [ ]
icon图片存在且符合规范 (128x128 PNG) - [ ]
README.md内容完整 - [ ]
CHANGELOG.md已更新 - [ ] 版本号已更新
- [ ] 所有测试通过
Q4: 如何进行灰度发布?
bash
# 预发布版本
vsce publish --pre-release
# 用户可选择安装 Pre-Release 版本
# Marketplace 上会显示 Pre-Release 标签