Emmet自定义缩写必须修改snippets.json文件而非.sublime-snippet XML文件,因二者属不同系统;正确路径为Emmet插件目录下的emmet/snippets.json,修改后需新建HTML文件验证,且须确保语法模式为HTML。

Emmet自定义缩写必须改 snippets.json,别碰用户 Snippet XML 文件
很多人想加个 myhtml5 缩写,就去 Tools → Developer → New Snippet… 写 XML,结果死活不生效。这是因为 Emmet 的 HTML/CSS 缩写逻辑完全走 snippets.json,和 Sublime 原生的 .sublime-snippet 是两套系统,混用等于白配。
正确路径是找到 Emmet 插件目录下的 emmet/snippets.json —— Windows 通常在 C:\Users\{user}\AppData\Roaming\Sublime Text 3\Packages\Emmet\emmet\snippets.json,macOS 在 ~/Library/Application Support/Sublime Text 3/Packages/Emmet/emmet/snippets.json。直接编辑这个文件才有效。
- 修改前先备份原文件,避免 JSON 格式错误导致 Emmet 整体失效
- 只改
"html": { "abbreviations": { ... } }里头的内容,不要动外层结构 - 新增项必须用英文双引号包裹键名和字符串值,
'!!': '!!!+html[lang=zh-CN]...'这种单引号会解析失败 - 改完保存,不用重启 Sublime,但要新建一个 HTML 文件再试,旧文件可能缓存了旧规则
html:5 和 ! 不是硬编码,它们都来自 snippets.json 的 abbreviations
你输入 html:5 能展开,不是因为 Emmet “内置了这个指令”,而是它在 snippets.json 里对应着一条配置:"html:5": "!!!+html[lang=${lang}]>(head>meta[charset=${charset}]+meta:compat+meta:vp+title{${1:Document}})+body"。同理,! 只是另一条映射,值一样或略有简化。
这意味着你可以安全覆盖默认行为:比如把 html:5 改成自动带 Bootstrap CDN 和 id="app" 的 div:
"html:5": "!!!+html[lang=zh-CN]>(head>meta[charset=UTF-8]+meta:compat+meta:vp+title{${1:Document}}+link[href=https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css rel=stylesheet])+body>div#app"
-
${charset}、${lang}这类变量,实际取值来自snippets.json同级的variables字段,可统一维护 - 如果删掉
html:5这一项,再输html:5就真没反应了——它不是“保留字”,只是预设缩写 - 别试图用
html:5触发 JS 注入之类操作,Emmet 不执行脚本,只做纯文本替换
自定义缩写后 Tab 不生效?先确认语法模式和触发方式
改完 snippets.json 发现新缩写(比如你加的 myheader)按 Tab 没反应,90% 是卡在两个地方:当前文件没识别成 HTML,或者 Tab 被别的插件劫持了。
右下角显示 Plain Text 或 Text 就直接无效,必须是 HTML(注意大小写,Html 或 html 都不行)。快捷修复:Ctrl + Shift + P → 输入 Set Syntax: HTML → 回车。
- 验证是否真生效:输入
html:5,按Tab看是否还能展开,能说明 Emmet 整体正常 - 如果
html:5行、你的myheader不行,大概率是snippets.json格式错了一处逗号或括号,打开 JSONLint 粘贴检查 - 坚持用
Ctrl + E替代Tab触发,它绕过所有补全冲突,是调试自定义缩写的首选方式 - 别在已有内容的行中间输缩写,Emmet 要求光标在空行或紧跟在空白符后,否则可能被当作普通文本忽略
变量和嵌套语法在自定义缩写里照常可用,但别滥用 $ 序号
${charset}、${1:Document}、ul>li*3 这些语法在自定义缩写里完全支持,不是只有官方缩写才特供。但要注意边界:
比如你想写 "nav:main": "nav.nav-main>ul>li*${count:3}>a[href=#]{Item $1}",本意是让用户输 nav:main 后按 Tab,再输数字覆盖 ${count:3}。但 Emmet 不支持运行时交互式变量赋值——${count:3} 会被直接替换成 3,$1 也只会生成 Item 1,不会弹出编号输入框。
-
${1:xxx}只控制光标初始位置和占位文本,不能动态改数量 - 真正可控的序号只有
*后面的数字,如li*5固定生成 5 个,li*${count}中的${count}必须是variables里预设的静态值 - 嵌套符号
>、+、^全部可用,但别嵌太深,超过 4 层容易读错且难调试 - 加 CSS 类或 ID 时,
div.container#app比div[class=container][id=app]更稳,后者在某些 Emmet 版本里解析异常
Ctrl + E 试 → 看控制台有没有报错。漏掉任一环,都会让你以为代码错了。

















