Establish a Baseline Before Blaming Anything
The precondition for any diagnosis is turning a vague feeling into data. Run `Developer: Show Running Extensions` to list each extension activation time, `Developer: Toggle Performance` for typing latency and render times, and note which helper process owns the CPU.
Separating renderer from extension host matters. High typing latency with idle extension hosts points at the renderer or a language server such as tsserver; sustained high CPU in `Code Helper (Plugin)` almost always names the culprit. Remember the role split visible in Task Manager: `Code Helper (Renderer)`, `Code Helper (Plugin)`, and `Code Helper (Watcher)` — the last one owns file watching exclusively.
A mandatory step is capturing a process and memory snapshot with `code --status`, then reproducing in an empty window: `code --disable-extensions .`. If the project is still slow with every extension disabled, the cause lies in editor settings, file count or the workspace itself.
# 空窗口打开,禁用全部扩展——最直接的归因手段
code --disable-extensions .
# 只跑单个扩展,用于二分定位
code --disable-extensions --locate-extension ms-python.python
# 查看当前会话进程与内存快照
code --status
# 界面内操作
# 命令面板 -> Developer: Show Running Extensions
# 命令面板 -> Developer: Toggle Performance
# 命令面板 -> Developer: Open Extension Host LogsBisecting Extensions
With few extensions (under 20) disabling them one by one is fastest. With many, bisect: move all extensions to a temp directory, restore half, and repeat until one extension remains. Bisection turns N trials into log2(N) — 40 extensions need only 6 rounds.
An even better daily routine uses three commands. Sort `Developer: Show Running Extensions` by activation time and suspect the slowest; run `Developer: Restart Extension Host` and see whether the lag disappears; then check `Developer: Open Extension Host Logs` for repeated errors or loops.
Once identified there are three responses: upgrade (many perf issues are fixed upstream), pin the version via a lockfile so the whole team matches, or recommend disabling through `"unwantedExtensions"` in `extensions.json`. Note that a recommendation is not enforcement — users can still install it.
// .vscode/extensions.json —— 团队统一扩展集合与禁用建议
{
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
"unwantedExtensions": [
{
"identifier": {
"id": "some.heavy-extension"
},
"recommendation": "该扩展在大型仓库中显著拖慢启动,请在团队内统一禁用"
}
]
}
// 二分定位脚本骨架(Bash)
// 1) 备份当前扩展目录
// 2) 循环:恢复一半扩展 -> code --disable-extensions . 复现 -> 记录卡顿程度files.exclude and search.exclude: Scoping Indexing and Search
These two settings are constantly confused. `files.exclude` hides files from the explorer and tree; `search.exclude` skips files during search. Glob patterns (not plain paths) let you filter by filename or extension precisely.
What actually costs performance is generated directories that are only implicitly excluded: `.git`, `node_modules`, `dist`, `build`, `.next`, `target`, `vendor`. VS Code excludes some by default, but once you `Add Folder to Workspace` or adopt a monorepo layout, the defaults are rarely enough.
A sound principle: exclude generated directories from the tree to reduce watching and rendering pressure, and exclude huge binary directories from search to avoid reading megabytes of content per query. Enable `search.useIgnoreFiles: true` and `search.useGlobalIgnoreFiles: true` so `.gitignore` and `.ignore` apply — this single pair fixes most search stalls.
// settings.json
{
"files.exclude": {
"**/.git": true,
"**/node_modules": true,
"**/dist": true,
"**/build": true,
"**/.next": true,
"**/target": true,
"**/.venv": true,
"**/*.log": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/build": true,
"**/target": true,
"**/*.min.js": true,
"**/*.map": true,
"**/pnpm-lock.yaml": true
},
"search.useIgnoreFiles": true,
"search.useGlobalIgnoreFiles": true,
"search.smartCase": true
}Tuning Indexing and File Watching on Large Repositories
In repositories with hundreds of thousands of files, the file watcher saturates inodes and CPU. The three most effective Linux-side moves: point `files.watcherExclude` at generated directories, prefer native recursive watching (`files.watcherUsePolling: false` — never enable polling by default), and on container or network filesystems use a gVisor-style FS to reduce inode events.
TypeScript projects need dedicated tuning: raise `typescript.tsserver.maxTsServerMemory` (e.g. 4096), set `typescript.disableAutomaticTypeAcquisition: true` to stop auto-fetching `@types` packages that trigger a second indexing pass, and consider `typescript.tsserver.useSyntaxServer` for the lighter syntax-only server.
Two underused editor switches matter too: `editor.codeLens: false` and `editor.minimap.enabled: false` measurably reduce rendering cost on large files. Keep `files.hotExit` at its default so closing a window does not serialize unsaved state and fake a stall. After changing settings run `Developer: Reload Window` to rebuild the index, then compare `Developer: Toggle Performance` readings.
// 大仓库 settings.json
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/node_modules/**": true,
"**/dist/**": true,
"**/build/**": true,
"**/.next/**": true
},
"files.watcherUsePolling": false,
"typescript.tsserver.maxTsServerMemory": 4096,
"typescript.disableAutomaticTypeAcquisition": true,
"editor.codeLens": false,
"editor.minimap.enabled": false,
"files.hotExit": "onExitOnly",
"search.followSymlinks": false
}