Nuemark 语法完全参考:Nue 内容优先标记语言详解
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
Nuemark 是 Nue 项目中面向内容创作者的 Markdown 扩展格式,它保留了标准 Markdown 的全部能力,同时新增了区块(sections)、块(blocks)、标签(tags)、脚注与自定义组件等面向现代网页开发的语法结构。本文以仓库 packages/www/docs/nuemark-syntax.md 为骨架,结合 packages/nuemark 包的真实源码与测试用例,系统讲解 Nuemark 的每一种语法形态、其底层解析逻辑与最终 HTML 输出,帮助你直接用 Markdown 写出结构清晰、组件化、可交互的内容页面。
一、Nuemark 在项目中的位置
Nuemark 是独立的核心包之一,入口在 packages/nuemark/index.js。它对外暴露三个主要 API:
nuemark(content, opts):把一段 Markdown 字符串直接渲染成 HTML;parseNuemark(content):把文档解析为结构化的 AST(blocks、meta、headings、codeblocks等),供程序化查询;parseSize/renderIcon等工具函数。
解析过程分为两段:先由parseBlocks/parseDocument把文本拆成块级与行内 token,再由renderBlocks/renderInline输出 HTML。整条管线不依赖任何运行时框架,既可以在构建期渲染成静态页面,也可以作为服务端 API 使用。如果你想先了解整体设计理念,可阅读 Nuemark 介绍 与 项目 README。
二、文件结构与 Front Matter
Nuemark 文件使用.md扩展名,文件头部可以携带可选的 YAML front matter,用于注入页面元数据:
--- title: My Page date: 2024-01-15 tags: [web, design] --- # Page content starts herefront matter 的元数据可以被布局、组件和构建系统读取。从源码实现看,front matter 的提取与解析发生在 src/parse-document.js 的stripMeta函数中:它扫描文件前几行,找到成对的---定界符,把中间内容拼接后交给nueyaml包的parseYAML解析,因此所有标准 YAML 类型(字符串、数字、布尔、数组、嵌套对象)都受支持。
stripMeta之后,parseDocument还会自动做两件补充:
- 自动标题:如果 front matter 没有
title,会从正文第一个h1(或标签块内的标题)提取标题; - 自动描述:如果 front matter 没有
description,会取第一个内容块的第一行作为描述。
也就是说,即使不写 front matter,文档对象依然可以拿到meta.title与meta.description,这对 SEO 和内容聚合非常友好。
三、标准 Markdown 支持
Nuemark 完整支持标准 Markdown 语法,包括各级标题、段落、加粗/斜体/行内代码、无序/有序列表(含嵌套)、多行引用、链接、图片等:
# Heading 1 ## Heading 2 ### Heading 3 This is a paragraph with **bold** and *italic* text, plus `inline code`. - Unordered list item - Another item - Nested item 1. Ordered list 2. Second item > Blockquote with multiple lines > continues here [Link text](https://example.com) Alt text在块级解析(src/parse-blocks.js)中,行首字符决定了块的类型:#开头识别为标题、-/*/数字.开头识别为列表项、>开头识别为引用、|...|识别为表格、```识别为围栏代码块,其余非空行按连续段落合并。引用与列表支持嵌套递归解析,例如测试 test/block.test.js 验证了- item内再缩进- nested会生成多层<ul><li>结构。
另外,代码解析器会跳过以<开头的行和//开头的注释行,这些内容不会进入输出。
代码块与语法高亮
围栏代码块通过语言标注启用高亮:
```js function hello() { return "Hello world" }支持的常见语言包括 JavaScript、TypeScript、Python、HTML、CSS 等,高亮由内置的 [Nueglow](https://link.gitcode.com/i/38444fd5df71280332ce4708b0a09eaf) 语法高亮引擎完成。从 [src/render-blocks.js](https://link.gitcode.com/i/dd3ada8164366ce38498805a0f122000) 的 `renderCode` 可以看到:语言名会被存入 tag 的 `name`,代码正文交给 `glow(code, { language, numbered })` 处理,因此你还可以: - 通过 `numbered` 数据项开启行号; - 通过 `caption` 或 `_` 提供代码标题,此时输出会包一层 `<figure>` + `<figcaption>`; - 为代码块指定 class,用于定制样式。 ## 四、增强的格式化语法 在标准 Markdown 之外,Nuemark 提供了一套紧凑的格式化标记,其映射关系定义在 [src/parse-inline.js](https://link.gitcode.com/i/0501a45875e04569828684970540600a) 的 `FORMATTING` 表中: ```md **bold** or __bold__ → <strong>bold</strong> *italic* or _italic_ → <em>italic</em> `code` → <code>code</code> ~strikethrough~ → <s>strikethrough</s> "quoted text" → <q>quoted text</q> \|highlighted| → <mark>highlighted</mark>特殊字符•(bullet)提供非语义的加粗:
•bold text• → <b>bold text</b>格式化解析器有一些值得注意的细节(均有测试覆盖):***与___会生成<em><strong>嵌套;格式化标记内部不允许首尾空白;如果闭合标记后紧跟单词字符则不视为格式化(避免误伤**x-bold**之类的英文连写);反引号包裹的代码会转义<、>后再输出,防止注入。
五、标题与属性
Nuemark 允许在标题末尾用花括号语法附加 id 和 class,用于样式与锚点链接:
# Nuemark: Content-first web development { .heroic } ## Nuemark Introduction { #intro } ## How to use Nuemark { #howto.heroic }生成结果:
<h1 class="heroic">Nuemark: Content-first web development</h1> <h2 id="intro">Nuemark Introduction</h2> <h2 id="howto" class="heroic">How to use Nuemark</h2>行内解析器对{ #id.class }这种形态会生成is_attrtoken,交给parseAttr提取 id 与 class(见 src/parse-tag.js)。如果渲染时传入heading_ids选项,renderHeading还会为没有显式 id 的标题自动生成锚点 id(规则见createHeadingId:取前 32 个字符、去撇号、非单词字符转连字符、统一小写),并包一个<a href="#id">锚点链接。parseDocument返回的headings数组也会为每个标题补全id,方便程序化生成目录(TOC)。
六、变量插值
用花括号嵌入动态值:
Current version: { version } Page title: { title } Author: { author }变量可以访问渲染上下文提供的数据(opts.data),以及 front matter 中定义的元数据。底层实现在 src/render-inline.js 的renderVariable:它对表达式执行new Function('data', 'return data.' + expr),因此支持点号访问嵌套属性,例如{ site.author.name };取值失败时静默返回空字符串,不会中断渲染。
需要说明的是:花括号同时承担了“标题属性”语法({ #id.class }),解析器会根据花括号内首字符是#/.还是普通变量名来区分两者。
七、区块(Sections)与包装
自动分节
开启sections: true后,Nuemark 会根据标题层级自动把内容包进语义化的<section>:
--- sections: true --- # Introduction First section content... ## Features Second section content... ## Technical Details Third section content...生成:
<article> <section> <h1>Introduction</h1> <p>First section content...</p> </section> <section> <h2>Features</h2> <p>Second section content...</p> </section> <section> <h2>Technical Details</h2> <p>Third section content...</p> </section> </article>分节算法在 src/parse-document.js 的sectionize中:它寻找第一个h1–h3之间的标题或---分隔符作为切分依据;若首个标题是h3,则以每个h3为一节,否则以h1/h2级别切分。完全没有标题或分隔符的文档不会产生 section。
区块类名
用数组形式为每个 section 依次指定 class:
--- sections: [hero, features, details] ---渲染时第 i 个 section 会带上classList[i],因此你可以对不同区块应用不同的背景、字号或网格样式。
手动分节
如果你希望完全控制切分点,可以用三连短横线---显式分隔:
First section content... --- Second section content... --- Third section content...---在块级解析中被识别为is_separator(注意它同时是水平线***/___等主题分隔符中的一种),sectionize遇到分隔符必然开新节。
区块包装
通过section_wrapper可以给每个 section 内部再包一层容器,便于控制内容的最大宽度:
--- section_wrapper: wrap ---生成:
<section> <div class="wrap"> <!-- content here --> </div> </section>这对“通栏背景 + 居中内容”的版式非常实用:<section>负责背景铺满,.wrap通过max-width约束内容宽度,从而实现更精细的设计控制。在源码中该选项名为content_wrapper(见parseDocument的renderSections)。
八、块(Blocks)语法
块语法用[.类名]把一段内容包进带指定 class 的<div>,类名完全由你的设计系统决定:
[.note] ### Important Note This content is wrapped in a div with class "note"生成:
<div class="note"> <h3>Important Note</h3> <p>This content is wrapped in a div with class "note"</p> </div>任何类名都可以使用:
[.warning] → <div class="warning">...</div> [.testimonial] → <div class="testimonial">...</div> [.pricing-tier] → <div class="pricing-tier">...</div> [.photo-gallery] → <div class="photo-gallery">...</div>实现上,[.name]被解析为名为block的内置标签(见 src/render-tag.js),缩进的内容递归解析为子块;attr.popover存在时甚至会输出<dialog>元素。
自动嵌套 div
块内部会根据第一个出现的标题级别自动分组生成嵌套 div。下面的例子中首个标题是h3,所以每个h3都会开启一个新的嵌套 div:
[.features] ### Feature One First feature description ### Feature Two Second feature description<div class="features"> <div> <h3>Feature One</h3> <p>First feature description</p> </div> <div> <h3>Feature Two</h3> <p>Second feature description</p> </div> </div>也可以使用三连短横线---显式创建嵌套 div:
[.testimonials] "Great product!" - Sarah Chen --- "Changed our workflow" - Michael Park<div class="testimonials"> <div> <p>"Great product!"</p> <p>- Sarah Chen</p> </div> <div> <p>"Changed our workflow"</p> <p>- Michael Park</p> </div> </div>这正是sectionize在块级内容上的复用:block标签渲染时对子块调用sectionize,有多个分组时每组包一个<div>(实现见 src/render-tag.js 的block())。
常见模式
类名随设计系统而定,以下是两个常用模式:
网格布局——响应式多列:
[.grid] ### Feature One First feature description ### Feature Two Second feature description ### Feature Three Third feature description堆叠布局——纵向排列、间距一致:
[.stack] ### Design Focus on systematic design ### Engineering Built for performance ### Content Pure content structure这些模式之所以成立,是因为你的 CSS 定义了.grid、.stack的行为;Nuemark 只负责结构,表现层完全交给设计系统,内容里不会出现任何 div 堆叠或内联样式。
嵌套块
块可以无限嵌套,自由组合:
[.feature] ## Main Feature Feature description [.grid] ### Sub-feature A Description A ### Sub-feature B Description B九、标签(Tag)语法
标签(tag)用方括号扩展出富组件能力,是 Nuemark 组件化的核心:
[tagname options]选项格式
命名属性:
[image src="photo.jpg" alt="Description" loading="eager"]纯值(作为默认参数):
[image photo.jpg]**嵌套 YAML:**把标签体作为 YAML 数据块解析(源码中isYAML会识别key: value或列表形态):
[image] src: photo.jpg alt: Description caption: Photo captionID 与类名:
[image#hero.responsive photo.jpg]解析逻辑在 src/parse-tag.js 的parseTag中,值得注意的规则:
- 属性值支持单引号/双引号(
valueGetter会把带空格的字符串保护起来); id、class、hidden、disabled、popover等白名单属性(ATTR)以及data-*前缀的键会写入 HTML 属性,其余键进入data(组件数据);- 纯值会存入
_键,即“默认参数”; - 布尔值:
loop、muted这类无值的键等价于true;字符串"true"/"false"/数字会自动转换类型(parseValue); - 以
:开头的键(如:rows="pricing")会被解析为数据绑定,渲染时从全局数据中取值(extractData)。
嵌套内容
标签可以携带嵌套内容,其中依然可以使用 Markdown:
[note] This is nested content that becomes part of the component. Markdown **works** here.嵌套内容与嵌套 YAML 的区别在于:标签体若整体是合法的key: valueYAML 则作为数据;否则作为 Markdown 内容递归解析(见 src/parse-blocks.js 的processNestedBlocks)。测试 test/block.test.js 对这两种形态都有断言。
十、内置标签
图片
基本用法:
[image photo.jpg]带说明文字(支持 Markdown):
[image photo.jpg] This is the image caption with **markdown** support响应式图片(small/large分别用于移动端与桌面端):
[image] small: mobile.jpg large: desktop.jpg alt: Responsive image图片链接:
[image photo.jpg] href: /gallery/ caption: Click to view gallery从源码(src/render-tag.js 的image())可以看到实现细节:
loading默认lazy;- 提供了
small时,会输出<picture>+ 两个<source>,分别带max-width/min-width媒体查询,断点默认750px(可用offset覆盖),并补<img>兜底; - 有
href时整张图包进<a>; - 有
caption或嵌套内容时补<figcaption>,最终始终包在<figure>中; size="400x300"之类的尺寸会被parseSize解析为width/height属性,帮助浏览器预留布局空间、避免 CLS。
视频
基本用法:
[video intro.mp4]带选项:
[video] src: intro.mp4 poster: thumbnail.jpg autoplay: true muted: true loop: truevideo()会依据文件扩展名推断 MIME 类型(mp4/webm/mov 等映射表见MIME常量),并透传autoplay controls loop muted poster preload src width这些原生属性;嵌套内容可作为<video>的 fallback 文本。
表格
增强表格语法:
[table] Name | Email | Role Alice | alice@example.com | Developer Bob | bob@example.com | Designer带标题、表头与表尾:
[table caption="Team Members"] Name | Email | Role ------ Alice | alice@example.com | Developer Bob | bob@example.com | Designer ------ Total: 2 team membersrenderTable会自动识别------分隔出的表头(thead)与表尾(tfoot),并处理单元格合并(列数不足时自动colspan);caption输出<caption>;单元格内支持行内 Markdown。配合数据绑定,你还可以用[table :rows="users"]直接渲染站点数据里的表格。
内联 SVG
把 SVG 图标内联进句子:
Continue reading [svg /icons/arrow-right.svg]svg()通过readIcon读取.svg文件(路径省略扩展名时会自动补.svg),并把根元素加上class="icon"后内联输出,因此图标可以直接被 CSS 着色与缩放。另一个icon标签则支持 symbol 引用方式(输出<svg class="icon icon-xxx"><use href="#xxx"/></svg>)。
十一、手风琴(Accordions)
手风琴非常适合 FAQ 或任何需要渐进式披露的内容:
[accordion] ## First Question Answer to the first question ## Second Question Answer to the second question ## Third Question Answer to the third question它生成使用原生<details>/<summary>的语义化 HTML,无需任何 JavaScript:
<div> <details> <summary>First Question</summary> <p>Answer to the first question</p> </details> <details> <summary>Second Question</summary> <p>Answer to the second question</p> </details> <details> <summary>Third Question</summary> <p>Answer to the third question</p> </details> </div>与块语法相同,手风琴按遇到的第一个标题级别或---分隔符分组(内部同样复用sectionize)。
手风琴选项
name—— 给一组手风琴命名,使同一时刻只展开一个:
[accordion name="faq"]共享相同name的手风琴会自动联动:打开一个会关闭组内其他项(name会原样输出到<details>的 name 属性上)。
open—— 设置初始展开状态:
[accordion open] # First item open by default [accordion open="2"] # Second item open by default源码中open === true && i == 0 || i === open决定了第几项默认展开,其中i是从 0 开始的序号。
十二、脚注
标准脚注语法:
This needs clarification[^1]. [^1]: This is the footnote content.命名脚注:
[Separation of Concerns][^soc] is fundamental. [^soc]: Keeping HTML, CSS, and JavaScript separate.脚注引用会渲染为带role="doc-noteref"的上标序号,文档末尾自动生成带role="doc-endnotes"的<ol>列表(实现见 src/parse-document.js 的renderFootnotes),语义化与无障碍标准保持一致。
你还可以用[define]标签配合带 id 的标题来定义术语,生成<dl>描述列表,并自动把这些 id 注册为可被[^term1]引用的脚注锚点:
[define] ## Term One { #term1 } Definition of term one ## Term Two { #term2 } Definition of term two十三、自定义组件
Nuemark 的扩展性体现在:开发者可以在 HTML 文件中定义自定义标签,内容作者只需用自然语法调用。组件文件以<!doctype html lib>开头:
<!doctype html lib> <!-- Button component --> <a :is="button" class="button { class }" href="{ href }"> { label || _ } </a> <!-- Card component --> <div :is="card" class="card { type }"> <h3>{ title }</h3> <slot/> <footer :if="footer">{ footer }</footer> </div>在 Markdown 中使用:
[button "Get Started" href="/docs/"] [card type="feature"] title: Key Feature footer: Learn more This is the card content with full **Markdown** support.组件属性
组件(标签)在渲染时接收以下数据来源:
- 命名属性:
[card type="feature"]中的type; - 未命名属性:通过
_访问,例如[button "Get Started"]中的文本"Get Started"; - 嵌套 Markdown 作为 HTML:通过
<slot/>标签注入,内容保持完整的 Markdown 渲染; - 页面元数据:来自 front matter;
- 站点数据:通过 .yaml 文件提供(可配合
:key="data.path"语法绑定全局数据)。
当某个标签没有对应的内置/自定义渲染函数时,Nuemark 不会报错,而是输出“客户端存根”(见 src/render-tag.js 的renderIsland):生成<tagname nue="tagname">元素并内联一段application/json数据脚本,交由客户端组件挂载,从而实现 SSR 与交互组件(islands)的平滑过渡。
十四、参考文件索引
- 语法文档原文:packages/www/docs/nuemark-syntax.md
- Nuemark 包入口与 API:packages/nuemark/index.js
- 块级解析:packages/nuemark/src/parse-blocks.js
- 文档级解析(front matter、分节、脚注):packages/nuemark/src/parse-document.js
- 行内解析(格式化、变量、链接、行内标签):packages/nuemark/src/parse-inline.js
- 块级渲染:packages/nuemark/src/render-blocks.js
- 内置标签渲染(image/video/table/svg/accordion 等):packages/nuemark/src/render-tag.js
- 标签解析(选项格式、数据绑定):packages/nuemark/src/parse-tag.js
- 测试用例:packages/nuemark/test/block.test.js、packages/nuemark/test/tag.test.js、packages/nuemark/test/inline.test.js
- 相关文档:Nuemark 介绍、Nueglow 语法高亮、HTML 文件类型
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考