VS Code 搜索与 Emmet 配置实战:避开 node_modules,让 JSX 补全更顺手

VS Code 搜索与 Emmet 配置实战:避开 node_modules,让 JSX 补全更顺手 不知道你们有没有这种经历在 VS Code 里想搜一个项目中的关键词结果等了几十秒出来一堆node_modules里的文件。用资源管理器找文件时也老被那一大坨依赖目录晃眼睛。另一件事是明明平时写 HTML 时 Emmet 飞快一进 JSX 文件输入div.container按 Tab 却一点反应都没有。这两个问题单独看都不大但放在一起恰恰是 VS Code 使用中最容易劝退新手的点一个像“系统卡死”一个像“功能失效”。我最早也被这两件事来回折磨过。后来把settings.json里几个关键项理清楚又弄明白 Emmet 的语法匹配机制后才算彻底清爽。这篇文章就把这两块一起聊聊搜索时如何正确避开node_modules以及 JSX/TSX 环境下如何把 Emmet 配置到“指哪打哪”的程度。顺带会解答几个容易翻车的细节比如为什么我排除了node_modules后还是搜不到结果或者为什么 Emmet 在特定文件后缀下总是失灵。1. 先解决搜索 node_modules 的苦恼1.1 为什么 VS Code 默认搜不到 / 会卡顿很多刚接触 VS Code 的朋友会嘀咕我明明按了CtrlShiftF为什么搜不到node_modules里的代码其实默认情况下VS Code 不是“搜不到”而是“默认忽略”。VS Code 的全局搜索底层是 Rust 写的 ripgrep它天生会读取项目里的.gitignore文件而node_modules几乎必然会被前端工程化模板写进.gitignore里。所以你在默认搜索时会发现结果列表很难出现依赖包源码项目越大越明显其实不算 bug是一个默认约定。但是这里有个很大的坑当项目里某个小文件被人误删或者你临时要排查“第三方包内部为什么报错”时你必须搜索node_modules。这时候直接取消 .gitignore 忽略又会导致搜索慢到爆甚至卡死。因为现在的 node_modules 动辄几百 MB里面还有数万个文件每搜一次就要跑几千个文件效率极低。所以正确的思路不是“删除node_modules再搜”而是给 VS Code 两个独立的开关设置。一个管“资源管理器里是否显示”一个管“搜索时是否包含”。很多人一上来直接改files.exclude把node_modules隐藏了但搜索时依然会慢因为search.exclude和files.exclude是两个维度。这点后文我会用表格详细拆。1.2 一套顺手的排除配置files.exclude、search.exclude、files.watcherExclude先说结论我现在的用户settings.json里与node_modules相关的配置是这样的{ // 资源管理器里隐藏 node_modules看着清爽 files.exclude: { **/node_modules: true, **/.git: true, **/dist: true, **/out: true }, // 全局搜索时跳过 node_modules避免卡死 search.exclude: { **/node_modules: true, **/dist: true, **/out: true }, // 文件监听排除 node_modules降低 CPU 和内存占用 files.watcherExclude: { **/node_modules/**: true, **/dist/**: true, **/out/**: true } }这三者分工不同我逐个说files.exclude控制左侧资源管理器里的“显示与隐藏”。把它设为 true 后node_modules不会出现在文件树里平时找代码不会误入依赖目录。search.exclude控制全局搜索CtrlShiftF时哪些目录被忽略。这个才是“搜索卡顿”的救星。注意它和 files.exclude 是独立的哪怕你把 node_modules 在资源管理器中显示出来这里不排除搜索照样会把整个依赖目录扫一遍。files.watcherExclude控制 VS Code 文件监听器忽略哪些路径。node_modules 里文件特别多默认监听会导致编辑器内存占用偏高尤其是 monorepo 项目。加上这个配置编辑器会明显轻快。提示files.exclude里如果把某个目录隐藏了不影响你通过CtrlP打开其中文件也不影响搜索除非search.exclude也排除了它。所以这两个设置并不冲突你完全可以“显示但搜索不包含”。1.3 针对“好不容易搜出来但结果还是很多”的经验有朋友会遇到另一种情况明明已经把node_modules排除了搜索某个关键词还是出现很多相似结果比如搜一个函数名出来几百条。这里不是没排除干净而是因为项目里可能有多个node_modules目录比如 pnpm 的软链接结构、monorepo 里的多个子包各自带依赖。或者是 npm workspace 结构根目录有一个node_modules每个子包也有独立的node_modules。你在设置里用的 glob 写法**/node_modules已经覆盖了所有层级所以正常情况下会全部排除。但如果某一个子包的目录名不叫node_modules而是比如.cache、.pnpm那就不会被排除。所以要顺手把一些常见目录也加入忽略名单我的习惯是这样search.exclude: { **/node_modules: true, **/bower_components: true, **/dist: true, **/out: true, **/.cache: true, **/.pnpm-store: true, **/coverage: true, **/.turbo: true, **/.nx: true }.coverage 是测试覆盖率目录.turbo 和 .nx 是构建缓存目录这些也都属于“没必要搜”的目录。你可以在团队里统一一个网友使用习惯避免每个人自己维护一份容易漏配的清单。另外如果你像我一样喜欢用CtrlP快速找文件而不是去资源管理器点那 files.exclude 里甚至可以藏得更多比如各种*.min.js、.map文件。这一步做完后搜索速度快到飞起。我之前在一个 Vue3 中型项目上实测过如果没排除 node_modules一次全项目搜索大概要 15 到 20 秒加上这些排除项后基本 1 秒内出结果。这个体验差距非常明显。2. JSX 里 Emmet 不生效配置思路和原理2.1 Emmet 到底靠什么识别语法先问一个问题你在.html文件里输入ulli*3然后按 Tab能顺利展开。为什么切到.jsx文件后同样操作没反应因为 Emmet 默认启用的语法列表里是没有javascriptreact和typescriptreact这两种语言的。VS Code 内置的 Emmet 扩展只对 html、css、scss、less 这类传统前端语言默认开启。JSX 后缀的文件会被 VS Code 识别成javascriptreact或typescriptreact而这两种语法不在 Emmet 的默认“白名单”里。解决思路很简单把 JSX/TSX 关联到 Emmet 支持的语法上通常是html。同时如果希望 JSX 属性里也能用 Emmet 写 className 等还需要做一些语法配置。具体原理上emmet.includeLanguages这个设置项的作用是把语言标识符映射到 Emmet 识别的语言标识符。可以用javascriptreact: html这种写法。这里“javascriptreact”是 VS Code 内部识别.jsx文件后使用的语言 ID“html”是 Emmet 插件的目标语法。设置后Emmet 就会按照 HTML 的语法去解析.jsx文件里的缩写。2.2 推荐的 JSX / TSX Emmet 配置下面这组配置是我目前在用的直接放进用户设置或工作区设置里都能生效{ emmet.includeLanguages: { javascript: javascriptreact, typescript: typescriptreact, javascriptreact: html, typescriptreact: html }, emmet.syntaxProfiles: { html: { inline_break: 3, attr_quotes: double, self_closing_tag: true }, jsx: { self_closing_tag: true, attr_quotes: double } }, emmet.triggerExpansionOnTab: true }逐项说明emmet.includeLanguages把.js文件映射到javascriptreact这样做的好处是某些项目里 JSX 写在.js文件里也能被正确识别。.ts文件同理映射到typescriptreact。然后javascriptreact和typescriptreact都映射到html这是 Emmet 展开 HTML 缩写的核心。emmet.syntaxProfiles这里配置的是 HTML 和 JSX 语法的输出风格。attr_quotes设为double所以属性用双引号self_closing_tag设为true方便img /、input /这类标签自动补全成自闭合格式。有些同学喜欢单引号也可以改成single看团队规范。emmet.triggerExpansionOnTab这个开关控制按 Tab 时是否触发 Emmet 展开。默认是 true但如果你之前改过记得拉回来。如果你用的是自定义键盘快捷方式确保没占用 Tab 键。配置完成后你再在.jsx文件里输入div.container按 Tab应该能直接展开为div classNamecontainer/div。这里有个细节Emmet 默认把class属性转成 JSX 里的className实际上这个能力是 VS Code 内置的 JSX 支持做到的吗并不完全是。Emmet 本身有一个内置的jsx语法配置它会根据 syntaxProfile 里的jsx选项来决定输出时是否把class改写为className所以在 includeLanguages 映射到 html 的同时还需要确保 syntaxProfiles 里有jsx的配置否则某些版本下会输出成classcontainer并不符合 JSX 规范。2.3 Vue 3 中使用 JSX 或 TSX 时的额外补充Vue 3 项目里用 JSX 的场景越来越多此时.jsx或.tsx文件的语言 ID 依然是javascriptreact或typescriptreact所以上一节的配置同样适用。但 Vue 官方在模板里的语法是.vue文件如果你在.vue文件的template区块里用 Emmet默认是正常的因为 VS Code 对.vue文件内部块级语言实际上会做混合解析template 部分识别为 html。值得注意的是Vue 3 中如果你是用render函数写 JSX比如export default defineComponent({ setup() { return () div classappHello/div } })此时同样受益于上面的 includeLanguages 配置。如果你希望这种 JSX 写法中的class自动变成className那么上面 syntaxProfiles 里的jsx配置也会起到作用。不过如果你的项目用了vitejs/plugin-vue-jsx编译时会自动处理 class 到 className所以不配置也问题不大。但配置之后至少编辑器的展开效果和代码风格是一致的不会出现缩写出错或补全后明明语法不对的情况。一个小坑在 Vue 项目里如果你同时安装了 VolarVue 官方插件和旧版 Vetur两边的语言服务可能会打架导致.vue文件里的 Emmet 有时生效有时不生效。我的建议是 Vue 3 项目只装 Volar把 Vetur 禁用掉不然你会排查到怀疑人生。3. 实操用 settings.json 一次性配好的完整方案3.1 工作区设置与用户设置怎么选配置放在哪个层级这是很多新手会忽略的问题。用户设置作用于所有项目适合比较通用的排除规则比如**/node_modules几乎每个前端项目都有依赖目录统一排除很省心。工作区设置仅作用于当前项目放在.vscode/settings.json鸡肋的是它适合团队里每个人统一某些项目专属规则比如某个项目里有个vendor目录别的项目没有这时候写在工作区设置里更合适团队其他人拉下代码后会自动生效不用每个人手动配。从我个人的经验来看像node_modules、dist、out这种是全局通用规则建议写用户设置。像某个项目特有的packages/legacy目录、某个老项目里的resource/static目录才建议写工作区设置。毕竟没人希望打开同事的老项目时自己的全局搜索里冒出一堆无关的目录。另外一个实用技巧如果你在团队里想让全组统一搜索排除规则可以使用工作区设置然后在代码评审时把它作为项目工程规范的一部分固定下来。这样比让大家各自改 user settings 要可靠得多。3.2 完整配置示例下面是我目前使用的完整配置几乎不用改就可以直接复制到你的settings.json{ // 资源管理器和搜索排除 files.exclude: { **/node_modules: true, **/.git: true, **/.svn: true, **/.hg: true, **/CVS: true, **/.DS_Store: true, **/dist: true, **/out: true, **/.cache: true, **/coverage: true }, search.exclude: { **/node_modules: true, **/bower_components: true, **/dist: true, **/out: true, **/.cache: true, **/.turbo: true, **/.nx: true, **/coverage: true }, files.watcherExclude: { **/node_modules/**: true, **/dist/**: true, **/out/**: true, **/.cache/**: true, **/.git/**: true }, // Emmet 配置 emmet.includeLanguages: { javascript: javascriptreact, typescript: typescriptreact, javascriptreact: html, typescriptreact: html, vue-html: html, plaintext: pug }, emmet.syntaxProfiles: { html: { inline_break: 3, attr_quotes: double, self_closing_tag: true }, jsx: { self_closing_tag: true, attr_quotes: double } }, emmet.triggerExpansionOnTab: true, // 编辑器基础行为 editor.tabCompletion: on, editor.snippetSuggestions: top }稍微解释下我加的两个额外项plaintext: pug如果你平时会在纯文本文件里写 Pug 模板这个映射可以让 Emmet 对纯文本文件也生效。如果你不用 Pug这一行可以删掉。editor.tabCompletion: on这个和 Emmet 关系不是直接的但加上后普通 snippet 补全也可以按 Tab 展开避免被 Emmet 的优先触发抢占掉。如果你发现某些时候按 Tab 没反应可能就是这个设置没开。3.3 配置后实测验证的几个方法配置完成后建议花 30 秒验证一下效果。验证搜索排除是否生效打开一个前端项目按CtrlShiftF。在搜索框里输入node_modules中常见的包名比如lodash或react结果里应该不会出现node_modules路径下的文件。前提是项目恰好装了这个依赖。如果想验证某个全局配置是否被正确加载可以使用命令面板CtrlShiftP输入Preferences: Open User Settings (JSON)再和你刚刚设置的代码对比。验证 Emmet 是否生效新建一个test.jsx文件输入div.app然后按 Tab如果能展开成div classNameapp/div说明最关键的 includeLanguages 和 syntaxProfiles 都生效了。再输入ul.listli.item*3按 Tab应该展开成一个包含 3 个li的无序列表。如果只有第一条生效、第二条没反应说明 Tab 触发设置或 snippet 优先级有问题。可以进一步测试属性缩写比如输入input:checkboxJSX 里应该展开为input typecheckbox /确认自闭合标签和属性引号是否符合预期。注意以上验证如果在 0.5 秒内没有任何反应首先检查文件右下角语言模式是否已经识别成 JavaScript React / TypeScript React。如果 VS Code 识别错了比如把.jsx识别成JavaScript那 Emmet 也可能不生效。4. 踩坑记录几个容易忽略的细节4.1 files.exclude 和 search.exclude 到底差在哪这是一个高频误区。我见过很多同事认为“我把node_modules在资源管理器里隐藏了那搜索的时候肯定也不会搜到它”然后抱怨搜索依然卡。这里要明确一下设置项作用范围是否影响搜索是否影响资源管理器files.exclude编辑器资源管理器、文件选择器基本不影响隐藏目录/文件search.exclude全局搜索和快速打开CtrlP的匹配范围完全排除不影响显示files.watcherExclude文件监听不影响搜索不影响显示如果只设置了files.exclude哪怕目录被隐藏抽空搜索时 ripgrep 还是会扫描它的实际内容。所以搜索卡顿要优先看search.exclude。另外search.exclude里设了 true 之后CtrlP快速打开文件时也不会匹配到这个目录里的文件。这样依赖包内的源码就真的“不可见”了如果你临时要搜一个依赖包里的源码可以从这处排除。临时要搜依赖包里的代码怎么办我一般会这样操作在search.exclude里暂时注释掉对应规则搜索完改回来。如果你嫌麻烦也可以直接在全局搜索框输入node_modules/xxx我记得 VS Code 的搜索其实会跳过这些目录。确实不行你可以在工作区临时新建一个 setting 覆盖一下这个方案笨但有效。4.2 为什么 search.useIgnoreFiles 会“漏掉”搜索结果search.useIgnoreFiles是 VS Code 搜索中另一个常用配置它默认开启意思是搜索时读取.gitignore、.ignore等文件并跳过其中声明的路径。很多人会遇到一个诡异现象明明项目里的.gitignore没有忽略某些目录但搜索时结果就是不完整排查半天发现是.ignore文件里加了规则或者全局限定的search.useIgnoreFiles被别的插件改了。如果你希望搜索一定包含“被 git 忽略的但确实存在”的目录可以在search.exclude里明确把那个目录设为false。例如search.exclude: { **/node_modules: true, **/coverage: false }这样可以让覆盖优先级更高避免被 .gitignore 逻辑影响。这个写法很多人不知道但其实很有用。4.3 使用 .gitignore 和 .ignore 文件的边界开篇提到 VS Code 默认搜索会跟随 .gitignore 规则那么如果你有一种情况是需要“让 VS Code 搜索除了 node_modules 之外的其他被忽略目录”你不需要去改 .gitignore也不要直接删掉项目的 .gitignore只需要在项目根目录单独建一个.ignore文件把「VS Code 搜索应该忽略但 git 需要忽略」或者反过来管理的目录写进去。.ignore文件的语法和.gitignore一样但它是众多工具包括 ripgrep读取的通用 ignore 规则优先级高于 .gitignore。它是独立文件不影响 Git 的提交规则。这个设计很巧妙你可以把dist、coverage、node_modules写进.ignore它只对编辑器工具链生效而 .gitignore 仍然决定 Git 的忽略内容。这样做的好处是团队里其他人拉下项目后也会生效因为.ignore文件会跟着代码仓库提交。4.4 团队协作时配置的同步问题最后讲一个深坑。你本地配好了search.exclude和emmet.includeLanguages但同事拉下代码后可能还是会出现“搜索卡死”或“JSX Emmet 不生效”的问题因为用户设置是个人本地的不会跟着仓库走。如果是小团队建议把公共配置直接放到项目的.vscode/settings.json里这样所有克隆仓库的人都会自动套用。不过要注意团队配置不应包含个人习惯性配置比如字体大小、主题只放与项目相关的规则。而像 Emmet 这种比较通用的配置放在用户设置里比较好毕竟项目无关和你个人习惯有关。我的实际用法是项目级的.vscode/settings.json主要放search.exclude、files.exclude以及部分工作区特殊规则用户级settings.json放 Emmet 映射、还有editor.tabCompletion等通用编辑器行为。这样互为补充不会互相覆盖。5. 最后再分享一个实操小技巧上面讲的其实都是非常常规的配置最后补一个冷门但很实用的技巧如果某个项目里node_modules结构太复杂比如 pnpm 生成的符号链接导致files.watcherExclude仍然感觉卡顿你还可以在 VS Code 命令面板里执行Developer: Reload Window来重启窗口有时候比反复改配置管用得多。另外如果你工作里经常需要在多个 Node.js 项目之间切换建议把node_modules的search.exclude和files.watcherExclude配置成全局用户设置而不是每个项目都去设置一遍。我刚配置完的时候最大的感受就是搜索从“等待”变成了“秒出”写 JSX 时的补全也终于不迟钝了。这两处小改动前后不到五分钟但每天打开 VS Code 的体验真的判若两人。个人经验是VS Code 很多“不好用”的瞬间其实不是编辑器的问题而是默认配置和你的使用习惯没对齐。像 node_modules 搜索和 JSX Emmet 这种都属于“一天要碰到十次”的小事花几分钟改一下 settings.json长期回报非常划算。如果你配完之后发现某个场景还是不对优先检查语言模式是否识别正确再看是不是有别的插件把按键或配置顶掉了。排查思路理顺了这类问题基本都能在几分钟内解决。