
1. 快速认识 Emmet为何它是我最推荐的编码效率工具如果你每天都要写 HTML却还在一个字母一个字母地敲标签那你和那些用 Emmet 一分钟生成整页结构的人差的不是手速而是一套缩写语法。我记得第一次看同事敲div.boxul.listli.item*5然后按下 Tab整段嵌套结构瞬间展开当时我整个人是愣住的。那是很多年前在 Sublime Text 时代插件还叫 Zen Coding后来升级为 Emmet如今已经被 VSCode、WebStorm、Sublime Text 这些主流编辑器直接内置几乎不需要额外配置就能用。Emmet 解决的核心痛点只有一个把写 HTML/CSS 时的重复机械输入量降到最低。它的思路是用一段类似 CSS 选择器的缩写表达出你想要的 DOM 结构再用 Tab 或快捷键展开成完整代码。这个思路之所以这么多年不过时是因为它没有锁死任何框架或 UI 库生成的永远是干净的标准标签代码不管你是写原生 HTML、Vue 模板、React 的 JSX还是小程序结构它都能直接帮上忙。这篇文章适合谁如果你刚入门前端尽早养成用 Emmet 的习惯能把宝贵时间花在结构和逻辑思考上而不是浪费在闭合标签上如果你已经写了几年代码但一直只会在编辑器里手动复制粘贴前面的片段那这里面有很多进阶写法可以帮你提速就算你是偶尔改改页面的后端同学掌握几个核心缩写也足以节省大量时间。下面我会从环境、语法、实战、踩坑四个维度完整讲一遍。1.1 它到底好在哪从手速竞争切换到语法表达写前端页面最消耗耐心的地方其实不是 CSS 布局也不是交互逻辑而是大段的重复结构。典型如后台管理系统一页往往就是几十个表格行或者表单字段这些结构长得相似却又有细微差异复制粘贴很容易出错手动敲又非常低效。Emmet 的出现让这件事变成了“写选择器”。比如你想生成一个三列表头配五行数据行的表格用手敲大概需要三十多次回车和标签闭合用 Emmet 只需要一行table.tabletheadtrth*3^^tbodytr*3td*3然后 Tab 展开。它把“结构关系”压缩成了嵌套、并列、^向上退层这几个符号让设计到实现的翻译过程变得很直接。另外别小看了$这个序号占位符。批量生成带有递增 id 或 class 的列表时它就像一个简单版模板引擎。举个例子li.item$3*4展开后是 item3、item4、item5、item6你完全不用手动改数字。这种小幅度的自动化在实际业务里攒起来效率提升非常可观。1.2 编辑器生态你的工具够用吗Emmet 在主流编辑器中的支持情况简单说就是“默认内置”和“插件安装”两大类绝大多数场景不需要折腾VSCode直接内置默认通过 Tab 键展开设置项里搜emmet就能看到完整配置WebStorm 全家桶内置且对 Emmet 的支持非常深连文件类型扩展都考虑到了Sublime Text 3/4需要安装 Emmet 插件属于老牌标配Neovim/Vim有第三方的 emmet-vim 插件设置稍多但体验完整在线编辑器 CodePen、JSFiddle 以及一些低代码平台的 HTML 编辑区很多也集成了 Emmet 展开能力。提示如果你用的是 VSCode打开设置搜索emmet.includeLanguages把vue、javascriptreact这些文件类型关联进去才能在对应文件里使用 HTML 缩写。这个坑很多人一开始都会踩。2. 从一条缩写到完整 HTMLEmmet 核心语法骨架我始终觉得Emmet 的语法并没有多少需要死记硬背的地方它本质上就是“CSS 选择器 几个符号”。只要你会写.class和#id剩下的就是在记忆几个符号的语义。2.1 最常用符号一览 ^ * () {} $表示子级例如navulli生成三层嵌套表示同级例如divp生成并列的两个标签^表示向上退一级例如divp^spanspan会回到div的父级与div并列*表示重复例如li*5生成五个li()表示分组可以让一部分结构先算好再展开例如(headernav)main{}表示插入文本内容例如p{你好}$表示自动递增序号配合*使用效果最佳。这里面最容易被忽略的是^的用法。很多人写多层嵌套时想要跳回上一层只会不断加括号或者拆开写其实用一个^就能解决。比如div.wrapheadernav^^footer展开后footer不在header里而是和header并列属于div.wrap的子级。如果需要跳两层就写两个^。虽然实际项目里跳两层的情况不算多但理解这个规则会帮你轻松读懂一切复杂缩写。2.2 默认值和隐式标签少打字的关键Emmet 有一条很贴心的规则叫做“隐式标签”。当你省略标签名时它会根据父级自动推断单独的.box会展开成div classbox/div因为默认元素是div在ul或ol下直接写.item会展开成li classitem/li在table下写.row会推断为tr classrow/tr在select下写.opt会推断为option classopt/option。这个规则非常实用。最典型的场景就是列表生成ul.breadcrumb.item*3比ul.breadcrumbli.item*3少敲几个字符并且语义同样清晰。刚开始你可能不太敢用因为不确定展开结果但实际上你只需要记住一个检查方法缩写的最后一级如果只有一个.或#开头的选择器Emmet 会按上下文自动补标签如果上下文无法推断一律用div。2.3 属性、文本和自闭合标签的处理除了 id 和类名Emmet 还能直接在缩写里写属性。中括号[属性值]就是干这个用的。比如a[hrefhttps://example.com][target_blank]会生成一个带完整属性的链接。在实际项目中最常用的其实是配合输入框生成表单input[typeemail][nameuser_email][placeholder邮箱]。文本内容用花括号{}包裹比如button.primary{确认提交}展开成button classprimary确认提交/button。需要注意 Emmet 的文本不会做特殊转义如果你要放入很多中文字符并且里面还混合了特殊符号建议展开后再检查一遍。至于自闭合标签input、img、br等标签 Emmet 会按 HTML 规范正确生成一般不需要手动处理。实操心得很多人喜欢在缩写里写很长的中文默认内容比如p{这里是一段默认文案}。我一般不建议这样做因为展开之后你还得手动删。更好的做法是先只建结构把文本占位符留空或者用lorem系列占位文本。p*3lorem一行就能生成三段标准的英文占位段落真正改文案时反而省心。3. HTML 高频写法实战直接抄作业语法说到底只是工具真正有价值的是你在业务里怎么用它。这一节我会直接放一些我平时在项目中反复使用的写法每一段都对应一个典型场景你可以直接复制到编辑器里体验。3.1 页面骨架、导航和板块结构新建一个 HTML 文件输入!再按 Tab能直接生成 HTML5 文档骨架包括!DOCTYPE html、html、head里的meta、title以及body。以前还需要手动补字符集和视口配置现在一行搞定。拿到骨架之后最常见的需求是快速搭出页面的整体区块header.site-headernav.navbar.nav-item*4a[href#]这一行展开后就是一个带四个链接的顶部导航结构。再配合mainsection*3把主体区域分成三块一个静态页面的基本框架就在两三次 Tab 之间成型了。很多人觉得 Emmet 只适合做小 demo实际上后台系统那种几十个模块的页面反而更需要这种快速生成结构的方式。3.2 表单页面的批量生成表单页面是我个人觉得 Emmet 性价比最高的应用场景之一。你想想看注册页里通常有用户名、邮箱、密码、确认密码、协议勾选、提交按钮如果是手打每块内容都是四五行代码的复制粘贴复制完还得改 name、id、placeholder。用 Emmet 可以写成一整段form.reg-form(.form-grouplabel{用户名}input[nameusername])(.form-grouplabel{密码}input[namepassword][typepassword])button.btn[typesubmit]{注册}这样展开后每个表单项都有清晰的 class 分层属性也是你预先设计好的省掉的不只是打字时间更是来回检查属性是否抄错的时间。我在实际项目里还会搭配 Vue 或 React 的受控组件写法把v-modelform.username或value{form.username} onChange{handleChange}写进属性里展开即模板基本不用二次修改。3.3 表格与复杂结构的整体生成表格是 Emmet 分组语法最好的演示场景因为它天然就是嵌套加重复的组合。老式后台管理系统最常遇到的就是表格页面table.table.table-hovertheadtrth*4^^tbodytr*3td*4注意这里^^的作用是让tbody回到table层级而theadtrth*4这个分组不需要括号也能正确展开因为^^已经把层级退回来了。如果你希望多个td里放不同的按钮还可以在td内部继续嵌套tabletbodytr*2td*3button.btn{操作}看到没td*3button.btn{操作}会给三列都生成操作按钮。这种批量生成能力配合多光标编辑几乎可以覆盖所有表格类需求。3.4 结合 JSX 和 Vue 模板的使用技巧如果你在 React 或 Vue 项目里写 JSX要注意 Emmet 的展开目标和原生 HTML 不太一样。JSX 中你更多是生成组件结构比如div.cardh2.titlep.desc这样。但有一个大坑Emmet 生成的是标准 HTML 属性比如class而在 JSX 里需要写成className所以展开后经常要把class统一改成className。在 Vue 模板中template里可以使用标准 HTML Emmet而script setup部分则不行因为那是 JS 上下文。VSCode 里需要在设置中把javascriptreact或typescriptreact加入emmet.includeLanguages否则在.jsx文件里按 Tab 没有反应。实操心得在 Vue 中我习惯把组件名作为 Emmet 的标签名记忆例如el-form、el-input、a-button这种它们在 Emmet 中会被当作合法标签并正确展开成el-form/el-form。不过要注意的是展开后如果标签内容为空部分格式化插件可能给你强行补成自闭合建议展开后立刻让格式化插件处理一次。4. CSS 缩写实战样式代码也能快到离谱大多数人学 Emmet 会先学 HTML 部分然后发现一个惊人的事实CSS 的缩写才是真正让你“上瘾”的功能。因为在 CSS 里一行属性就够了Emmet 对属性名的缩写力度却非常大输入几个字母直接出整条属性。4.1 CSS 缩写的底层逻辑真的不用死记Emmet 的 CSS 缩写不是瞎编的它基本遵循“属性名的首字母 关键部分”这个规律。比如margin记成mpadding记成pwidth记成wheight记成hfont-size记成fzline-height记成lhbackground-color记成bgc。这些缩写本身就像拼音或英文单词的缩写看到基本能猜个七八成。但有几点得提醒你不要随意在缩写字后面乱加东西。比如你想写margin: 20px auto;的话可以直接输入m20auto但为了可读性更推荐m0-auto它会把连字符作为两个值之间的分隔点。值里面不能带空格因为空格是编辑器常见的触发方式在缩写里你需要用-或直接连接来表示多值。4.2 高频 CSS 缩写速查表我把日常项目里出现频率最高的缩写整理成一份表方便你贴在手边查缩写展开结果w100width: 100px;w100pwidth: 100%;h10height: 10px;m0margin: 0;m0-automargin: 0 auto;p10-20padding: 10px 20px;mt20margin-top: 20px;p10padding: 10px;fz14font-size: 14px;fw700font-weight: 700;lh1.5line-height: 1.5;dfdisplay: flex;dibdisplay: inline-block;posaposition: absolute;posrposition: relative;bdborder: ;继续补值bdrborder-right: ;bdrs4border-radius: 4px;bgc#fffbackground-color: #fff;bxshbox-shadow: ;注意上表里的bdr是border-right如果要写圆角习惯上是bdrs。这俩容易混淆我第一次用时也翻过车后来在 VSCode 里给自己配了自定义补全才彻底解决。4.3 单位、颜色和响应式关键的几个用法值带单位的处理方式是 Emmet 设计得比较巧妙的一部分。默认情况下w100生成的是像素单位100px这是 Web 开发最常用的单位。而当你需要百分比时用p结尾比如w50p生成width: 50%;用e结尾是em用r结尾是rem比如fz1.5r生成font-size: 1.5rem;。颜色值可以直接跟在缩写后面c#333会生成color: #333;bgc#f5f5f5会生成background-color: #f5f5f5;。这里有个小坑如果颜色值作为第三个或第四个值出现比如box-shadow: 0 0 4px rgba(0,0,0,0.3)这种带括号和逗号的长值Emmet 的解析可能不符合预期我更推荐先展开一个空属性再用编辑器的颜色选择器去补色值。至于!importantEmmet 是在缩写的最后写!再展开比如m0!得到margin: 0 !important;。这个很实用但也容易产生滥用风险。我的原则是只有在覆盖第三方 UI 库强制样式时才用自己的代码里尽量避免否则后面维护时排查优先级会非常痛苦。4.4 渐变、动画、flex 等复杂属性的写法很多进阶教程会讲到一些复杂属性的快捷写法但实际用到频率并没有想象中高。我的经验是Emmet 对 flex 布局相关的支持足够好用比如df生成display: flex;jcc生成justify-content: center;aic生成align-items: center;。这几个配合起来几乎是现代前端布局的日常主力非常值得背下来。渐变和动画这种属性缩写虽然也能生成骨架但值非常长还不如先写background:然后用编辑器自带的颜色选择器去点。因此我不会硬记bg后面那一大串建议大家把 Emmet 用在“结构搭建”和“高频属性”上真正复杂的长属性交给代码补全和颜色选择器这才是效率最大化。5. 进阶场景让 Emmet 融入真实工作流学会缩写的语法之后很多人的提升瓶颈就不是“会不会”而是“用不用”了。这一节我会讲讲怎么把 Emmet 揉进日常开发流程里让它成为你肌肉记忆的一部分。5.1 多光标与 Emmet 的组合技最经典的一套操作是先展开结构再用多光标修改。比如你写了一个ulli.poster*6img[src]展开后六个img的src都为空。此时我把光标放到第一个src按CtrlD逐个选中或者直接用CtrlShiftL全选然后统一粘贴图片地址效率比复制粘贴整个标签高得多。Emmet 本身并不负责批处理流转数据你还需要配合altshift↑/↓复制行、CtrlD多选这些操作。我自己做列表页时通常是先用 Emmet 生成骨架再用多光标统一填充属性和文本一条 20 行的列表在 30 秒内完成非常舒服。5.2 写在编辑器外从设计图到 HTML 的翻译思维有些同学用 Emmet 很流畅但拿到设计图还是发呆因为不知道先写什么。这里我分享一个个人习惯拿到设计稿先做“区块拆解”把页面分解成“头部 / 主体 / 底部”这种大块再继续拆每一块里的小结构。拆完之后把每个区块翻译成一行 Emmet 缩写依次展开再填充样式和内容。比如看到一个卡片先在心里把它拆成“外层容器、图片、标题、正文、按钮”对应 Emmet 就是figure.cardimgfigcaptionh3pbutton。你并不需要背任何模板只需要掌握拆解的方式。这种思维方式比 Emmet 本身更值钱值得在日常开发中刻意训练。5.3 自定义 snippet把固定业务模板塞进 Emmet除了 Emmet 自带的语法它还支持自定义 snippets可以配置在全局的snippets.json中。你完全可以把自己项目里高频出现的结构定义成一行缩写比如mobilePage: div class\page\\n header class\page-header\.../header\n div class\page-content\.../div\n footer class\page-footer\.../footer\n/div这样每次新建页面只要输入mobilePage再展开就能得到完整的页面骨架。我前几年的项目里甚至把公司的组件模板全部定义成了 snippets从 30 多行重复代码变成一行展开收益极其可观。自定义 snippets 的优先级要高于默认语法配置时注意不要和现有缩写冲突。5.4 Emmet 和代码片段工具怎么分工写代码时很多人会把 Emmet 和编辑器自带的代码片段snippets搞混。简单区分Emmet 适合“动态生成有规律的结构”比如列表、表格、表单因为它的核心是表达式snippet 适合“固定的模板片段”比如一个组件的完整骨架因为文本不会经常变化。如果一个项目里大量使用固定模板比如公司后端管理系统的“列表页”和“表单页”我就用 snippets如果是每次数据不同的具体列表就用 Emmet。两者结合而不是二选一才是我实际工作流中最舒服的状态。6. 常见问题与踩坑实录照例分享几个我自己用 Emmet 时常遇到的问题和一些排查思路。这些问题看起来都很小但第一次遇到时确实会卡住。6.1 按 Tab 没反应先检查这几个地方最常见的原因是光标所在位置的问题。Emmet 展开必须把缩写放在一个可以被解析的位置比如行首、空格后或者标签之间的某个位置。如果你把缩写紧跟在一段中文后面有些编辑器就无法识别。还有一个原因是 VSCode 中虽然默认启用了 Emmet但文件类型没被识别也会静默失败。处理方法很简单分三步排查确认文件类型是 HTML 或已关联的类型看右下角状态栏把光标放在缩写末尾确认前后有空格或者干脆新起一行单独输入缩写在命令面板输入Emmet: Expand abbreviation看是否有展开结果。如果这三点都正常大概率是快捷键冲突需要去设置里手动把 Tab 键命令改成emmet.expandAbbreviation。6.2 展开结果和预期不一致的 3 个常见原因第一个原因是编号$的起始位置理解错了。$默认从 1 开始如果你想要从 3 开始得写$3。想要倒序可以写$-。我第一次用的时候以为$2表示从 2 开始结果发现不是花了半分钟才反应过来。第二个原因是隐式标签的推断。tabletr展开时tr是明确标签没问题但当你写table.row*3td*2时.row会默认被推断为tr这个行为是符合 HTML 语义的。不过在dl定义列表这种不那么常见的标签下推断结果可能不符合预期所以复杂的结构我还是建议把标签名写完整。第三个原因是括号导致的歧义。(divp)span和divpspan语义完全不同后者会把span放在p的兄弟位置前者则把span放在与整个div并列的位置。如果你展开后层级不对先回看自己是否少写了括号。6.3 值得养成的几个书写习惯最后说几个我踩过坑后总结出来的习惯。第一缩写尽量保持一行可读。超过三四个层级还憋在一行里自己回头看都费劲更别提和其他人协作。此时适合用分组或者干脆拆成两三段逐步生成。第二展开后立即格式化。很多格式化插件会自动把标签拆行让结构更清晰。我自己的习惯是生成后立刻执行一次格式化避免后面代码混乱。第三用 Emmet 但不被 Emmet 绑架。有些同事为了保证“一行生成整个页面”会在缩写里塞大量属性和文本结果展开出几百行的压缩代码格式化后还得大量手动修改。我的原则是结构越复杂缩写越短把细节留给多光标和后续编辑完成。对我个人来说Emmet 是极少数学了十年还在天天用的“小而美”工具。它不需要你花很长时间背诵却能持续地减少日常工作的重复感。如果你现在还处于手动敲标签的阶段真的建议从今天的下一个页面开始试着写第一段缩写。你可能会发现原来写结构也有一种接近“打字如飞”的快乐。