布局 API
布局用于控制一个 Markdown 页面最终输出的 HTML 外壳。
默认页面使用内置 default 布局;当页面需要首页、落地页、专题页等不同结构时,可以在 frontmatter 中通过 layout 切换布局。
目录约定
每个布局对应一个独立目录,目录名就是布局名。
内置布局由已安装的 vanilla-press 依赖包提供。项目侧可以在 vp/layouts/ 下新增或覆盖布局:
-
vp/
-
layouts/
-
landing/
-
template.html
-
style.css
-
script.ts
-
-
-
构建时会先读取内置布局,再读取 vp/layouts/ 中的项目布局。相同名称的项目布局会覆盖内置布局。
布局文件名是固定约定:
template.html:必填。没有该文件时,该目录不会被识别为布局。style.css:可选。存在时会合并进全站 CSS。script.ts/script.js:可选。存在时会打包为该布局的独立浏览器脚本;同时存在时优先使用script.ts。
新增一个布局
创建 vp/layouts/landing/template.html,基于 layout 对象定义模板变量:
HTML<main class="landing-layout"> <section class="landing-hero"> <p>{{ layout.hero.badge }}</p> <h1>{{ layout.hero.title }}</h1> <p>{{ layout.hero.description }}</p> </section> <article class="j-editor is-sm">{{{ content }}}</article></main>
创建 vp/layouts/landing/style.css:
CSS.landing-layout { width: min(1080px, calc(100% - 32px)); margin: 0 auto; padding: 48px 0;}.landing-hero { padding: 32px; border: 1px solid var(--ui-border); border-radius: 8px; background: var(--ui-surface-raised);}
如需为该布局添加浏览器脚本,可以创建 vp/layouts/landing/script.ts 或 vp/layouts/landing/script.js:
TypeScriptexport default function initLandingLayout(root: Document, config: unknown) { root .querySelectorAll('.landing-layout:not([data-layout-ready="true"])') .forEach((node) => { node.setAttribute('data-layout-ready', 'true') })}
布局脚本默认导出函数会在页面运行时调用,参数为 (document, runtimeConfig)。布局脚本会被打包为 dist/public/布局名.hash.js,只在使用该布局的 HTML 页面中加载。脚本中的静态 npm 依赖会复用 server.client.shared 和默认白名单:命中的依赖会打包进全局 runtime.js,未命中的依赖仍打包进该布局脚本文件。布局脚本也可以通过 vanilla-press/client 和 vanilla-press/client/modules/* 复用 vp/client 中的项目公共浏览器代码。
然后在 Markdown 页面中使用它:
Markdown---layout: landingtitle: 产品介绍layouts: landing: hero: badge: Release title: 新版本发布 description: 用一个自定义布局展示产品发布内容。---# 正文内容这里的 Markdown 会渲染到模板的 `{{{ content }}}` 插槽中。
模板变量
布局模板可以读取构建器注入的上下文。
| 变量 | 说明 |
|---|---|
{{ title }} |
当前页面标题,优先使用 SEO 标题 |
{{ description }} |
当前页面描述,来自 frontmatter |
{{ keywords }} |
当前页面关键词,来自 frontmatter |
{{ page.title }} |
Markdown 页面标题 |
{{ page.rel }} |
当前页面输出路径 |
{{ site.siteName }} |
vp/config/runtime.ts 中的站点配置 |
{{ layout.* }} |
当前布局作用域下的数据 |
{{ layouts.* }} |
所有布局作用域数据 |
{{{ content }}} |
Markdown 渲染后的 HTML |
{{{ editorHelp }}} |
编辑辅助块,包含编辑链接和最后编辑时间 |
{{{ slots.header }}} |
响应式站点头部,包含站点名、主菜单和操作组 |
{{{ slots.headerDocNav }}} |
窄屏文档导航插槽,包含侧边栏和目录按钮 |
{{{ slots.sidebar }}} |
默认侧边栏插槽 |
{{{ slots.mobileSidebar }}} |
手机端侧边栏抽屉内容插槽 |
{{{ slots.aside }}} |
默认右侧区域插槽,包含目录 |
{{{ slots.prevNext }}} |
分页导航插槽 |
{{{ slots.footer-info }}} |
页脚站点信息插槽 |
{{{ slots.social-list }}} |
社交链接列表插槽 |
普通双花括号会进行 HTML 转义,适合输出 frontmatter 中的文本。
三花括号不会转义,只用于构建器生成的可信 HTML 插槽,例如 content、editorHelp、slots.header、slots.headerDocNav、slots.sidebar、slots.mobileSidebar、slots.aside、slots.prevNext、slots.footer-info 和 slots.social-list。
数组循环
模板支持简单数组循环:
HTML<div class="actions"> {{#layout.hero.actions}} <a href="{{ link }}" class="{{ variant }}">{{ text }}</a> {{/layout.hero.actions}}</div>
对应 frontmatter:
YAMLlayouts: landing: hero: actions: - text: 快速开始 link: ./guide/quick-start.html variant: is-solid - text: 查看 API link: ./guide/api.html variant: is-soft
循环中的对象字段会提升到当前作用域,因此模板里可以直接写 {{ text }}、{{ link }}。如果数组项是字符串,可以使用 {{ this }} 输出当前项。
布局变量作用域
推荐把布局专用变量写到 layouts.<layoutName> 下:
YAMLlayout: landinglayouts: landing: hero: title: 自定义标题
当页面选择 layout: landing 时,模板中的 {{ layout.hero.title }} 会读取 layouts.landing.hero.title。这样可以避免多个布局之间的变量互相冲突。
分页导航插槽
server.prevNext 只会渲染到当前布局显式声明的插槽中:
HTML<div data-vp-prev-next></div>
默认文档布局已经包含该插槽。自定义布局如果不需要分页导航,可以不写这个插槽;如果需要,放在希望出现分页导航的位置即可。
默认布局参考
内置 default 布局复用文档站常规结构:左侧侧边栏、正文和右侧目录。它的模板核心结构如下:
HTML<header class="vp-header"> {{{ slots.header }}} {{{ slots.headerDocNav }}}</header>{{{ slots.mobileSidebar }}}<main class="{{ shell.className }}"> {{{ slots.sidebar }}} <section class="{{ shell.mainClassName }}"> <div class="vp-content" data-reveal> <div class="vp-content-wrap"> <article class="{{ shell.editorClassName }}" data-vp-editor> {{{ content }}} </article> {{{ editorHelp }}} </div> {{{ slots.prevNext }}} </div> {{{ slots.aside }}} </section></main>
如果新布局仍然是文档页,可以从这个结构复制后调整。{{{ slots.header }}} 和 {{{ slots.headerDocNav }}} 应放在 .vp-header 内部,{{{ slots.mobileSidebar }}} 应放在 header 后方,供窄屏侧边栏抽屉使用。如果新布局是首页或营销页,通常只保留 {{{ slots.header }}},然后自行设计页面主体。