在 VS Code 中创建自定义 Emmet 代码段

已发表: 2022-03-10
快速总结 ↬在本文中,Manuel 解释了为什么 Emmet 是他最喜欢的用于编写 HTML 和 CSS 的生产力工具之一,以及如何在 Visual Studio Code 中创建自定义 Emmet 片段以帮助您进一步改进前端工作流程。

今年早些时候,我在博客上分享了我在开始新的 Web 项目时喜欢使用的 HTML 样板,并逐行解释。 它主要是<head>标签和属性的集合,我通常在我构建的每个网站上使用。 直到最近,我只是在需要时复制并粘贴样板,但我决定通过将其作为片段添加到 VS Code(我选择的编辑器)来改进我的工作流程。

这是我创建的自定义片段的快速演示。

Visual Studio Code 中的片段和缩写

VS Code 内置了自定义用户片段以及 Emmet 提供的 HTML 和 CSS 片段和缩写。

例如,如果您在 HTML 文档中键入p>a{Sign Up}并按EnterTab ,Emmet 会将其转换为以下标记:

 <p><a href="">Sign Up</a></p>

注意访问 Emmet 文档以了解如何使用缩写语法。

如果我们经常需要这个特定的缩写,我们可以将它保存为一个片段,以进一步改进我们的工作流程。

 { "html": { "snippets": { "signup": "p>a{Sign Up}" } } }

现在我们可以输入signup并按EnterTab 键,我们将得到相同的结果。 我将在下一节解释如何创建片段。

Emmet 默认带有一堆 HTML 片段。 例如, ! 创建 HTML 文档的基本结构。

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> </html>

这很好,但是如果我们想通过删除或添加元素和属性来调整这个片段,我们必须覆盖它并创建我们自己的片段。

创建和覆盖片段

如果我们想创建自己的 Emmet 代码片段或覆盖 VS Code 中现有的代码片段,则需要执行以下步骤:

  1. 创建一个snippets.json文件,添加这个基本的 JSON 结构并将其保存在硬盘的某个位置。
     { "html": { "snippets": { } }, "css": { "snippets": { } } }
  2. 打开 VS Code 设置(代码 → 首选项 → 设置)并搜索“Emmet Extensions Path”。
  3. 单击“添加项目”,输入保存您之前创建的snippets.json文件的文件夹的路径,然后按“确定”。

而已。 现在我们准备通过向htmlcss对象添加属性来创建片段,其中key是片段的名称, value是缩写或字符串。

我的一些自定义 HTML 片段

在深入探讨片段创建并向您展示我是如何为我的 HTML 样板创建片段之前,让我们先用我创建的一些小但有用的片段来热身。

延迟加载

开箱即用,有一个img缩写,但没有用于延迟加载的图像。 我们可以使用默认缩写,只需在方括号中添加我们需要的附加属性和属性值。

 { "html": { "snippets": { "img:l": "img[width height loading='lazy']" } } }

img:l + Enter / Tab现在创建以下标记:

 <img src="" alt="" width="" height="" loading="lazy">

我创建的大多数页面由<header><main><footer>地标和一个<h1>组成。 自定义page缩写让我可以快速创建该结构。

 "snippets": { "page": "header>h1^main+footer{${0:©}}" }

page + Enter / Tab创建以下标记:

 <header> <h1></h1> </header> <main></main> <footer>©</footer>

这个缩写很长,所以让我们把它分解成更小的部分。

分解

创建一个<header>元素和一个子<h1>

 header>h1

向上移动,回到<header>的级别,并在<main>之后创建一个<footer> >。

 ^main+footer

<footer>中设置最后一个制表位并将默认文本设置为&copy

 {${0:©}}

导航

默认情况下,缩写nav只是创建一个<nav>开始和结束标记,但我通常需要的是一个带有嵌套<ul><li>元素和链接( <a> )的<nav> 。 如果页面上有多个<nav>元素,它们也应该被标记,例如使用aria-label

 "nav": "nav[aria-label='${1:Main}']>ul>(li>a[aria-current='page']{${2:Current Page}})+(li*3>a{${0:Another Page}})"

这看起来很疯狂,所以让我们再次分解它。

分解

我们从具有aria-label属性和嵌套<ul><nav>元素开始。 ${1:Main}使用文本“Main”填充属性,并通过将光标移动到属性值并在创建时突出显示它来在属性值处创建一个制表位。

 nav[aria-label='${1:Main}']>ul

然后我们创建四个带有嵌套链接的列表项。 第一项是特殊的,因为它使用aria-current="page"标记活动页面。 我们创建另一个制表位并使用文本“当前页面”填充链接。

 (li>a[aria-current='page']>{${2:Current Page}})

最后,我们添加了三个带有链接的列表项和链接文本“另一页”。

 (li*3>a>{${0:Another Page}})

在我们适应之前,我们得到了这个:

 <-- Before: nav + TAB/Enter --> <nav></nav>

现在我们得到这个:

 <-- After: nav + TAB/Enter --> <nav aria-label="Main"> <ul> <li><a href="" aria-current="page">Current Page</a></li> <li><a href="">Another Page</a></li> <li><a href="">Another Page</a></li> <li><a href="">Another Page</a></li> </ul> </nav>
跳跃后更多! 继续往下看↓

风格

默认style缩写只创建<style>开始和结束标记,但通常当我使用<style>元素时,我会这样做,因为我想快速测试或调试某些东西。

让我们为<style>标签添加一些默认规则:

 "style": "style>{\\* { box-sizing: border-box; \\}}+{\n${1:*}:focus \\{${2: outline: 2px solid red; }\\} }+{\n${0}}"

分解

某些字符(例如$*{} )必须使用\\进行转义。

 style>{\\* { box-sizing: border-box; \\}}

\n创建一个换行符, ${1:*}将第一个制表位放在选择器*处。

 {\n${1:*}:focus \\{${2: outline: 2px solid red; }\\}}
  • 之前<style><style>
  • 之后
     <style> * { box-sizing: border-box; }
    *:focus { outline: 2px solid red; } </style>

好吧,足够的热身。 让我们创建复杂的片段。 起初,我想为我的样板创建一个片段,但我创建了三个满足不同需求的缩写。

  1. 小的
  2. 中等的
  3. 满的

小样板

这是一个快速演示的样板,它创建了以下内容:

  • 基本网站结构,
  • viewport元标记,
  • 页面标题,
  • <style>元素,
  • 一个<h1>
 { "!": "{<!DOCTYPE html>}+html[lang=${1}${lang}]>(head>meta:utf+meta:vp+{}+title{${2:New document}}+{}+style)+body>(h1>{${3: New Document}})+{${0}}" }

分解

带有文档类型的字符串:

 {<!DOCTYPE html>}

带有lang属性的<html>元素。 lang属性的值是一个可以在 VS 代码设置(代码 → 首选项 → 设置)中更改的变量。

 html[lang=${1}${lang}]

您可以通过在 VS Code 设置中搜索“emmet variables”并更改lang变量来更改页面的默认自然语言。 您也可以在此处添加自定义变量。

VS Code 中有 2 个默认变量,lang 设置为 en,charset 设置为 UTF-8。

<head>包括charset元标记、 viewport元标记、 <title><style>标记。 {}创建一个新行。

 (head>meta:utf+meta:vp+{}+title{${2:New document}}+{}+style)

让我们先快速看看这给了我们什么。

 <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>New document</title> </head> </html>

看起来不错,但是meta:utf缩写在 HTML 中创建了旧的方式来定义charset ,而meta:vp创建了两个我不需要的制表位,因为我从不为viewport使用不同的设置。

在继续之前,让我们覆盖这些片段。

 { "meta:vp": "meta[name=viewport content='width=device-width, initial-scale=1']", "meta:utf": "meta[charset=${charset}]" }

最后但同样重要的是, <body>元素,一个带有默认文本的<h1> ,后跟最后一个制表位。

 body>(h1>{${3: New Document}})+{${0}}

最终样板:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>New document</title> <style> * { box-sizing: border-box; } *:focus { outline: 2px solid red; } </style> </head> <body> <h1> New Document</h1> </body> </html>

对我来说,这是完美的最小调试设置。

样板介质

虽然我仅将第一个样板用于快速演示,但第二个样板可用于复杂页面。 该片段创建以下内容:

  • 基本网站结构,
  • viewport元标记,
  • 页面标题,
  • .no-js / .js类,
  • 外部屏幕和打印样式表,
  • descriptiontheme-color元标记,
  • 页面结构。
 { "!!": "{<!DOCTYPE html>}+html[lang=${1}${lang}].no-js>{<!-- TODO: Check lang attribute --> }+(head>meta:utf+meta:vp+{}+title{${1: Change me}}+{}+(script[type=\"module\"]>{document.documentElement.classList.replace('no-js', 'js');})+{}+link:css+link:print+{}+meta[name=\"description\"][content=\"${2: Change me (up to ~155 characters)}\"]+{<!-- TODO: Change page description --> }+meta[name=\"theme-color\"][content=\"${2:#FF00FF}\"])+body>page" }

是的,我知道,这看起来像胡言乱语。 让我们剖析一下。

分解

doctype和 root 元素与第一个示例类似,但有一个额外的no-js类和一个注释,提醒我在必要时更改lang属性。

 {<!DOCTYPE html>}+html[lang=${1}${lang}].no-js>{ }

TODO Highlight 扩展使评论非常流行。

该扩展程序以视觉方式突出显示某些关键字,例如 TODO。

<head>包括charset元标记、 viewport元标记、 <title>{}创建一个新行。

 (head>meta:utf+meta:vp+{}+title{${1: Change me}}+{}

带有一行 JavaScript 的脚本。 我在 JS 模块支持上削减了芥末。 如果浏览器支持 JavaScript 模块,则意味着它是支持现代 JavaScript(例如模块、ES 6 语法、提取等)的浏览器。 我将大多数 JS 仅发送到这些浏览器,并且在 JavaScript 处于活动状态时,如果组件的样式不同,我会使用 CSS 中的js类。

 (script[type=\"module\"]>{document.documentElement.classList.replace('no-js', 'js');})+{}

两个<link>元素; 第一个链接到主样式表,第二个链接到打印样式表。

 link:css+link:print+{}

页面说明:

 meta[name=\"description\"\][content=\"${2: Change me (up to ~155 characters)}\"]+{ }

theme-color元标记:

 meta[name=\"theme-color\"\][content=\"${2:#FF00FF}\"])

正文元素和基本页面结构:

 body>page

最终样板文件如下所示:

 <!DOCTYPE html> <html lang="en" class="no-js"> <!-- TODO: Check lang attribute --> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> Change me</title> <script type="module"> document.documentElement.classList.replace('no-js', 'js'); </script> <link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="print.css" media="print"> <meta name="description" content=" Change me (up to ~155 characters)"> <!-- TODO: Change page description --> <meta name="theme-color" content="#FF00FF"> </head> <body> <header> <h1></h1> </header> <main></main> <footer>©</footer> </body> </html>

完整的样板

完整样板与第二个样板相似; 区别在于附加的meta标记和script标记。

该片段创建以下内容:

  • 基本网站结构,
  • viewport元标记,
  • 页面标题,
  • js / no-js类,
  • 外部屏幕和打印样式表,
  • description和开放图元标签,
  • theme-color元标记,
  • 规范的<link>标签,
  • 网站图标标签,
  • 页面结构,
  • < script>标签。
 { "!!!": "{<!DOCTYPE html>}+html[lang=${1}${lang}].no-js>{<!-- TODO: Check lang attribute --> }+(head>meta:utf+meta:vp+{}+title{${1: Change me}}+{}+(script[type=\"module\"]>{document.documentElement.classList.replace('no-js', 'js');})+{}+link:css+link:print+{}+meta[property=\"og:title\"][content=\"${1: Change me}\"]+meta[name=\"description\"][content=\"${2: Change me (up to ~155 characters)}\"]+meta[property=\"og:description\"][content=\"${2: Change me (up to ~155 characters)}\"]+meta[property=\"og:image\"][content=\"${1:https://}\"]+meta[property=\"og:locale\"][content=\"${1:en_GB}\"]+meta[property=\"og:type\"][content=\"${1:website}\"]+meta[name=\"twitter:card\"][content=\"${1:summary_large_image}\"]+meta[property=\"og:url\"][content=\"${1:https://}\"]+{<!-- TODO: Change social media stuff --> }+{}+link[rel=\"canonical\"][href=\"${1:https://}\"]+{<!-- TODO: Change canonical link --> }+{}+link[rel=\"icon\"][href=\"${1:/favicon.ico}\"]+link[rel=\"icon\"][href=\"${1:/favicon.svg}\"][type=\"image/svg+xml\"]+link[rel=\"apple-touch-icon\"][href=\"${1:/apple-touch-icon.png}\"]+link[rel=\"manifest\"][href=\"${1:/my.webmanifest}\"]+{}+meta[name=\"theme-color\"][content=\"${2:#FF00FF}\"])+body>page+{}+script:src[type=\"module\"]" }

这个令人难以置信的长片段创建了这个:

 <!DOCTYPE html> <html lang="en" class="no-js"> <!-- TODO: Check lang attribute --> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> Change me</title> <script type="module"> document.documentElement.classList.replace('no-js', 'js'); </script> <link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="print.css" media="print"> <meta property="og:title" content=" Change me"> <meta name="description" content=" Change me (up to ~155 characters)"> <meta property="og:description" content=" Change me (up to ~155 characters)"> <meta property="og:image" content="https://"> <meta property="og:locale" content="en_GB"> <meta property="og:type" content="website"> <meta name="twitter:card" content="summary_large_image"> <meta property="og:url" content="https://"> <!-- TODO: Change social media stuff --> <link rel="canonical" href="https://"> <!-- TODO: Change canonical link --> <link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.svg" type="image/svg+xml"> <link rel="apple-touch-icon" href="/apple-touch-icon.png"> <link rel="manifest" href="/my.webmanifest"> <meta name="theme-color" content="#FF00FF"> </head> <body> <header> <h1></h1> </header> <main></main> <footer>©</footer> <script src="" type="module"></script> </body> </html>

自定义 CSS 片段

为了完整起见,这里是我正在使用的一些 CSS 片段。

调试

此代码段创建一个带有自定义偏移的 5px 红色轮廓。

 "debug": "outline: 5px solid red;\noutline-offset: -5px;"

定心

display设置为 flex 并将其子项居中的片段。

 "center": "display: flex;\njustify-content: center;\nalign-items: center;"

position属性设置为sticky ,在topleft属性上有两个制表位。

 "sticky": "position: sticky;\ntop: ${1:0};\nleft: ${2:0};" 
应用于div元素的所有 3 个 CSS 片段的演示。

用户片段

在本文开头,我提到 VS Code 还提供了自定义代码片段。 与 Emmet 片段的不同之处在于您不能使用缩写,但您也可以定义制表位并使用内部变量。

如何充分利用用户片段可能是另一篇文章的主题,但这是我定义的自定义 CSS 片段的示例:

 "Visually hidden": { "prefix": "vh", "body": [ ".u-vh {", " position: absolute;\n white-space: nowrap;\n width: 1px;\n height: 1px;\n overflow: hidden;\n border: 0;\n padding: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n margin: -1px;", "}" ], "description": "A utility class for screen reader accessible hiding." }

当我们键入vh并按EnterTab时,此代码段不仅创建 CSS 规则,而且是整个声明块。

 .u-vh { position: absolute; white-space: nowrap; width: 1px; height: 1px; overflow: hidden; border: 0; padding: 0; clip: rect(0 0 0 0); clip-path: inset(50%); margin: -1px; }

最后的话

创建这些片段需要一些时间,但值得付出努力,因为您可以根据个人喜好自定义 Emmet,自动执行重复性任务并从长远来看节省时间。

我很想看看你使用了哪些片段,所以请在评论中与我们分享。 如果你想使用我的设置,你可以在 GitHub 上找到我最终的 snippets.json。

资源

  • 默认 CSS Emmet 片段
  • 默认 HTML Emmet 片段
  • 埃米特备忘单
  • VS Code 文档中的 Emmet