
Slidev 幻灯片布局系统全解析内置布局、加载优先级与自定义 Layout 编写【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidevSlidevPresentation Slides for Developers中布局Layout决定了每一页幻灯片的页面结构与视觉风格它本质上是一个包裹该页幻灯片内容的 Vue 组件。本指南围绕官方 docs/guide/layout.md 展开系统讲解如何通过 frontmatter 选用布局、Slidev 内置布局的完整清单与用法、内置/主题/插件/自定义布局之间的加载与覆盖规则以及如何亲手编写一个自定义 Vue 布局并与 slot 语法糖配合使用。读完本文你将能够在项目中自由组合布局、理解布局解析的底层机制并打造属于自己的可复用布局。Layout 是什么包在幻灯片外面的一层 Vue 组件在 Slidev 的渲染模型里每张幻灯片的内容Markdown Vue 语法会先被编译成一个独立的 Vue 组件而布局则是“包裹”这些内容的外层结构。官方文档给出的定义非常精炼Layouts in Slidev are used to define the structure for each slide. They are Vue components that wrap the content of the slides.也就是说写布局就是写 Vue 组件。布局负责页面骨架标题放哪里、正文放哪里、要不要左右分栏、背景是纯色还是图片而默认插槽slot /则负责承接你写在 Markdown 正文里的实际内容。从编译产物可以直观看到这层包裹关系在源码 packages/slidev/node/vite/layoutWrapper.ts 中每页幻灯片的模板内容会被改写成InjectedLayout v-bind_frontmatterToProps($frontmatter, index)包裹起来的结构这里的InjectedLayout正是动态 import 进来的布局组件见该文件的第 56 行import InjectedLayout from ...。因此你在页面上看到的一切结构都来自布局组件而 frontmatter 中除系统保留字段外的其余键值会被作为 props 传给布局。使用布局frontmatter 中声明 layout为某页幻灯片指定布局只需在该页的 frontmatter 中声明layout字段--- layout: quote --- A quote from someone对于quote布局来说页面会把正文当作一段引人注目的引用语来排版展示。布局名即组件名Slides 文件顶层第一页可以放置作用于全局的配置theme、addons 等而每一页也可以有自己的 frontmatter。默认布局规则如果你不显式指定布局Slidev 会给出两个预设默认值第一张幻灯片默认使用cover布局其余幻灯片默认使用default布局。该规则有明确的源码依据layoutWrapper.ts 在解析每一页时计算布局名const rawLayoutName data.slides[index]?.frontmatter?.layout ?? data.slides[0]?.frontmatter?.defaults?.layout let layoutName rawLayoutName || (index 0 ? cover : default)两处值得注意的细节除了单页的layout字段布局名还能通过第一页 frontmatter 中的defaults全局默认 frontmatter统一指定这非常适合整个演示统一替换默认外观若指定的布局名在所有来源中都找不到Slidev 会在终端打印Unknown layout ...错误并列出所有可用布局名随后回退到default布局而不是直接崩溃layoutName default。布局的slidev-layout前缀与slidev-layout default、slidev-layout cover这样的命名约定在 packages/client/layouts 下的每个内置布局模板根元素中都可以看到它们也是你在布局作用域样式中定位的关键钩子。布局的加载优先级后加载者覆盖先加载者官方文档明确指出布局按以下顺序解析最后加载到的同名布局会覆盖前面加载的同名布局内置布局见 docs/builtin/layouts.md主题theme提供的布局Addon插件提供的布局项目layouts目录中的自定义布局。这一规则在源码中体现得非常直白。看 options.ts 中createDataUtils暴露的getLayouts()它会遍历[resolved.clientRoot, ...resolved.roots]这一串根目录clientRoot即内置布局所在目录roots依序为「主题根目录、addon 根目录、用户项目根目录」见同文件 options.ts 的组合方式在每个根目录里通过 fast-glob 扫描layouts/**/*.{vue,js,mjs,ts,mts}文件布局名取文件名去掉扩展名后的 basename例如MyLayout.vue即MyLayout所有结果写入同一个layouts对象靠后的路径覆盖靠前的同名 key。由于用户项目根目录排在最后所以当你在自己的layouts/目录里放一个与内置布局同名的default.vue它就会覆盖内置default布局——这就是“自定义优先”的实现原理。同样的覆盖机制也解释了为什么主题可以重新定义内置布局主题根目录排在clientRoot之后。最终这些布局由虚拟模块/slidev/layouts收集成一个{ name: component }的对象导出给客户端使用见 packages/slidev/node/virtual/layouts.ts。补充主题与 addon 往往不只提供布局还会提供组件、全局样式等整套扩展能力。若需要了解主题如何覆写布局以及两者整体协作方式可进一步阅读 docs/guide/theme-addon.md。内置布局全景与用法详解Slidev 随仓库自带约 19 种内置布局源码全部位于 packages/client/layouts官方逐个说明见 docs/builtin/layouts.md。按其用途可分为四类分类布局适用场景通用型default、cover、intro、section、end、none演示的结构化节点封面、章节、结尾、纯内容页强调型fact、quote、statement、center让某句话/数据成为视觉焦点图像与网页型full、image、image-left、image-right、iframe、iframe-left、iframe-right以图片或内嵌网页为主的页面分栏布局two-cols、two-cols-header左右分栏、上通栏下分栏下面按类逐一展开。通用型default / cover / intro / section / end / nonedefault最基础的布局适合承载任意内容。其实现极其简单default.vue仅输出div classslidev-layout defaultslot //div。cover封面页用于展示演示标题、副标题、作者等开场信息cover.vue 同样只有一个带slidev-layout cover类名的插槽容器视觉样式由全局样式表负责。intro导言页通常也承载标题、简短介绍与作者信息。section标识一个新章节的开始常作为“章节过渡页”。end演示的结束页致谢、QA 等。none不带任何预设样式的最裸布局完全由你自己写样式。强调型fact / quote / statement / centerfact把某条事实或数据以大字号、强存在感的方式呈现在屏幕中央。quote以显著样式展示一段引言——这就是文章开头示例layout: quote的效果。statement将一句断言/宣言作为页面主体内容。center将内容垂直水平居中显示。这四类布局通常不需要额外 frontmatter 参数直接把正文写进去即可适合“一页一句话”的节奏型演示。图像与内嵌网页布局image / image-left / image-right 与 iframe 系列image系列布局将图片作为页面的视觉主体或侧栏。以 docs/builtin/layouts.md 中的用法为例image-left图片在左、正文内容在右--- layout: image-left # the image source image: /path/to/the/image # a custom class name to the content class: my-cool-content-on-the-right ---image-right图片在右、正文内容在左--- layout: image-right # the image source image: /path/to/the/image # a custom class name to the content class: my-cool-content-on-the-left ---image图片作为整页主内容正文可作为叠加内容--- layout: image # the image source image: /path/to/the/image ---这三个布局都支持通过backgroundSize覆盖默认的背景尺寸默认为cover可以传入任意合法的 CSSbackground-size值--- layout: image image: /path/to/the/image backgroundSize: contain ------ layout: image-left image: /path/to/the/image backgroundSize: 20em 70% ---从源码看这些 frontmatter 键与布局组件的 props 一一对应。image.vue 声明了image与backgroundSize默认cover两个 props而 image-left.vue 则额外声明了classprop并把背景应用在左侧图片容器、正文放在右侧slidev-layout default容器中。背景图的解析统一走 packages/client/layoutHelper.ts 的handleBackground()它会自动区分颜色值#hex或rgb...与图片 URL前者直接作为background后者写入backgroundImage并允许用参数dim叠加一层渐变压暗遮罩以保证文字可读性同时处理静态资源的 base 路径。iframe系列则是把一整个网页内嵌到页面中参数改为urliframe网页作为整页主体--- layout: iframe # the web page source url: https://example.com ---iframe-left/iframe-right网页在左/右半屏正文放在另一侧同样可用class自定义正文类名--- layout: iframe-left # the web page source url: https://example.com # a custom class name to the content class: my-cool-content-on-the-right ------ layout: iframe-right # the web page source url: https://example.com # a custom class name to the content class: my-cool-content-on-the-left ---分栏布局two-cols 与 two-cols-headertwo-cols将页面内容分隔为左右两栏栏内内容通过slot 语法糖::right::划分。官方用法--- layout: two-cols --- # Left This shows on the left ::right:: # Right This shows on the righttwo-cols-header则在上方保留一个横跨整行的通栏区第二行再左右分栏--- layout: two-cols-header --- This spans both ::left:: # Left This shows on the left ::right:: # Right This shows on the right style .two-cols-header { column-gap: 20px; /* Adjust the gap size as needed */ } /style官方示例中的style块用于演示如何针对布局根类名微调列间距可按需保留或删去。::name::是 docs/features/slot-sugar.md 定义的命名插槽速写语法::right::、::left::、::default::分别对应 Vue 的命名插槽。值得留意的是源码实现two-cols.vue 中左侧栏同时渲染默认插槽slot与命名插槽slot nameleft右侧栏只渲染slot nameright因此::right::之前的所有内容会自动落进左栏。它还声明了class应用到左右栏内容与layoutClass应用到根容器两个可选 prop。关于::right::、::default::、::left::的完整语义含显式指定::default::调整栏内容书写顺序的用法详见 docs/features/slot-sugar.md。编写自定义布局在 layouts 目录写 Vue 组件内置布局不够用时可以完全自定义。根据官方指南 docs/guide/write-layout.md只需要在项目根目录创建layouts/文件夹往里面放 Vue 组件即可your-slidev/ ├── ... ├── slides.md └── layouts/ ├── ... └── MyLayout.vue写完后即可在任意幻灯片 frontmatter 中通过layout: MyLayout使用它。因为布局本质就是 Vue 组件Vue 模板、指令、响应式逻辑等全部能力都可用包括script setup内的逻辑与useSlideContext提供的全局上下文。最基础的自定义布局只需一个默认插槽用于承接正文template div classslidev-layout my-layout slot / /div /template这与内置 default.vue、cover.vue 的结构完全一致。建议根元素保留slidev-layout前缀类名以继承 packages/client/styles/layouts-base.css 中提供的基准排版样式。使用命名插槽构造复杂布局需要多个内容区时可以声明命名插槽template div classslidev-layout split div classleft slot nameleft / /div div classright slot nameright / /div /div /template然后在幻灯片中用::left::/::right::即 docs/features/slot-sugar.md 的速写语法分别注入两侧内容。这样一个简单的布局组件就能同时服务多类页面。two-cols、image-left等内置布局正是基于这种“命名插槽 frontmatter 传参”的模式如果你希望自定义布局也接收class、自定义图片路径等参数同样可以为其声明 props。布局如何接收 frontmatter 参数前面提到布局会收到来自 frontmatter 的 props。其底层机制位于 packages/client/context.ts 的frontmatterToProps()export function frontmatterToProps(frontmatter: Recordstring, any, pageNo: number) { return { ...objectOmit(frontmatter, pageNo 0 ? HEADMATTER_FIELDS : FRONTMATTER_FIELDS), frontmatter, } }即把 frontmatter 中除系统保留字段如layout、clicks、transition、theme等见 packages/client/constants.ts 中的FRONTMATTER_FIELDS/HEADMATTER_FIELDS以外的键全部铺开为 props同时额外注入完整的frontmatter对象。所以你在 frontmatter 里写image: /img.png布局组件的defineProps({ image: String })就能拿到它而布局内若需访问全部 frontmatter则可用注入的frontmatterprop 或幻灯片上下文中的$frontmatter。这一点也提醒我们不要用与系统保留字段同名的键作为自定义布局参数否则它会被过滤而无法作为 prop 传递。布局作用域内还能做什么结合仓库中的其它扩展机制自定义布局还可以做得更强大点击步进与动态内容布局内可直接使用v-click等指令与点击计数机制相关源码在 packages/client/modules/v-click.ts让布局自带动态出现效果全局上下文通过 packages/client/context.ts 暴露的useSlideContext读取$slidev、$nav、$page、$clicks等运行态实现根据页码/点击次数变化的结构主题与 addon 自定义布局主题和 addon 通过相同机制注入布局并在加载顺序上先于项目自定义目录被扫描被后者覆盖要了解完整扩展生态可参阅 docs/guide/theme-addon.md。实战建议与常见问题遇到Unknown layout报错检查布局文件名与 frontmatter 是否完全一致大小写敏感因为getLayouts直接使用文件名 basename 作为 key终端打印的错误信息会列出当前所有可用布局名是排查主题/插件布局是否被正确安装的便捷途径。想让整个演示使用同一种非默认布局在第一页 frontmatter 的defaults中设置layout字段可让全部页面生效而不必逐页书写。需要临时覆盖内置布局在项目layouts/下放置同名文件即可如layouts/default.vue利用“最后加载者覆盖”的规则实现自定义默认外观同时保持 Markdown 正文零改动。图片/网页参数缺失时页面会异常使用image、iframe系列布局时务必同时提供image或url参数并确认资源路径以项目为基准可被正确解析。延伸阅读内置布局逐条说明与用法示例docs/builtin/layouts.md编写自定义布局完整指南docs/guide/write-layout.md布局命名插槽语法糖::left::/::right::/::default::docs/features/slot-sugar.md主题与 addon 如何扩展含覆写布局docs/guide/theme-addon.md内置布局源码packages/client/layouts布局解析与优先级实现packages/slidev/node/vite/layoutWrapper.ts、packages/slidev/node/options.ts、packages/slidev/node/virtual/layouts.tsfrontmatter 到 props 的传递机制packages/client/context.ts、packages/client/layoutHelper.ts【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考