ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Slidev 项目目录结构完全指南:以约定驱动组件、布局、样式与全局图层的扩展机制

Slidev 项目目录结构完全指南:以约定驱动组件、布局、样式与全局图层的扩展机制 Slidev 项目目录结构完全指南以约定驱动组件、布局、样式与全局图层的扩展机制【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidevSlidevPresentation Slides for Developers采用一套「约定优于配置」的目录结构约定用固定的文件与文件夹位置替代繁琐的配置项让开发者在几乎零配置的情况下即可扩展组件、布局、全局样式与页面图层。本文以官方文档 directory-structure.md 为骨架结合仓库源码packages/slidev/node与packages/client逐一剖析这些约定的加载机制与优先级帮助你正确组织自己的演示项目并能复现、验证每一步行为。目录约定总览在任意 Slidev 项目中默认约定的一级目录/文件结构如下your-slidev/ ├── components/ # 自定义组件 ├── layouts/ # 自定义布局 ├── public/ # 静态资源 ├── setup/ # 自定义 setup / hooks ├── snippets/ # 代码片段 ├── styles/ # 自定义样式 ├── index.html # 向最终 index.html 注入内容 ├── slides.md # 幻灯片主入口 └── vite.config.ts # 扩展 vite 配置以上所有目录与文件都是可选的全部缺席时 Slidev 也能直接运行纯 Markdown 幻灯片。其中slides.md是默认的演示入口vite.config.ts用于以 Vite 配置 的方式扩展构建链其余目录则分别对应本文接下来详解的扩展点。值得注意的一点是这些约定并非只对「用户项目根目录」生效。从 options.ts 的解析逻辑可以看到Slidev 会把主题目录themeRoots、插件目录addonRoots与用户目录userRoot合并为一组roots见 options.ts随后所有约定目录components、layouts、styles、setup、index.html等都会在每一个 root 中逐一查找并最终合并。这意味着主题与插件遵循同一套目录规范而你的项目目录拥有最高优先级的覆盖能力。自定义组件components/匹配规则./components/*.{vue,js,ts,jsx,tsx,md}放置在components/下的文件会作为全局可用组件无需手动 import直接在slides.md中书写即可# My Slide MyComponent :count4/组件来源共有三个层次见 组件使用指南Slidev 内置组件详见 内置组件对应源码位于 packages/client/builtin由主题与插件addons提供你项目components/目录下的自定义组件。其自动导入由unplugin-vue-components驱动源码位于 components.ts。关键实现如下见 components.tsComponents({ extensions: [vue, md, js, ts, jsx, tsx], dirs: [ join(clientRoot, builtin), ...roots.map(i join(i, components)), ], ... })几点与「目录约定」直接相关的结论dirs数组把内置组件目录clientRoot/builtin与每一个 root 下的components全部纳入扫描因此主题、插件、用户三方组件可以同名共存并合并支持.vue、.md、.js、.ts、.jsx、.tsx六种文件形态.md组件即 Markdown 写成的小型幻灯片片段由于用户 root 排在dirs末尾自定义组件在解析中处于相对靠后的位置这也是「用户目录拥有最终覆盖权」约定的一部分。如果你希望把组件打包成可复用的插件分享给他人可以参考 主题与插件Addon指南 与 编写 Addon。自定义布局layouts/匹配规则./layouts/*.{vue,js,ts,jsx,tsx}布局是包裹每一页幻灯片内容的 Vue 组件通过在幻灯片 frontmatter 中声明layout字段来使用见 布局指南--- layout: quote --- A quote from someone默认约定第一页使用cover布局其余页面使用default布局。内置布局列表见 内置布局源码位于 packages/client/layouts包含default.vue、cover.vue、two-cols.vue、section.vue、center.vue、quote.vue等。布局目录的扫描逻辑在 options.ts 的getLayouts中实现值得注意的实现细节扫描的 glob 是layouts/**/*.{vue,js,mjs,ts,mts}即支持子目录嵌套布局名取自basename去扩展名最终以「后写入者覆盖」的方式填入layouts映射扫描顺序为内置布局目录clientRoot→ 主题 → 插件 → 用户 root因此内置布局 → 主题布局 → 插件布局 → 自定义布局用户后加载者覆盖先加载者见 布局指南 与 options.ts。换句话说只要在项目layouts/下创建与内置布局同名的.vue文件即可整体替换该内置布局想从零编写布局可参考 编写布局。静态资源public/匹配规则./public/*该目录的语义与 Vite 的publicDir完全一致开发阶段目录内容以根路径/提供服务例如public/logo.png可用/logo.png访问构建阶段目录内容被原样拷贝到dist的根目录。源码中的对应实现位于 extendConfig.tspublicDir: join(options.userRoot, public),而主题与插件携带的public/目录则由 staticCopy.ts 使用vite-plugin-static-copy一并拷贝到构建产物实现「主题自带 logo/素材随构建输出」的能力。在实际演讲内容中你可以直接在 Markdown 或组件的src/href里使用根路径引用这些资源例如![](/logo.png)。关于 base path、远程图片等更细致的资源处理策略参见 资源处理 FAQ。自定义样式style.css/styles/匹配规则./style.css或./styles/index.{css,js,ts}命中约定的样式入口会被注入到应用根节点App root。若需要拆分多个 CSS 文件推荐使用styles/目录并在index.ts中手动管理导入顺序your-slidev/ ├── ... └── styles/ ├── index.ts ├── base.css ├── code.css └── layouts.css// styles/index.ts import ./base.css import ./code.css import ./layouts.css从源码看加载逻辑位于虚拟模块 conditional-styles.ts它对每个 root 依次尝试styles/index.{ts,js,css}、styles.{ts,js,css}、style.{ts,js,css}三种形态并全部导入见 conditional-styles.ts因此上表中的三种写法根级style.css、根级styles.css、目录化styles/index.*都是合法的入口。官方约定只枚举了style.css与styles/index.*其余两套是兼容性保障。样式会依次经过UnoCSS与PostCSS处理因此开箱即用地支持CSS 嵌套Nested CSSUnoCSS 指令transformer-directives如apply与--uno:快捷写法theme()函数读取 UnoCSS 主题色。一个典型的主题化全局样式示例官方文档示例.slidev-layout { --uno: px-14 py-10 text-[1.1rem]; h1, h2, h3, h4, p, div { --uno: select-none; } pre, code { --uno: select-text; } a { color: theme(colors.primary); } }::: warning 此处注入的全局 CSS同样会作用于演示者presenter界面。为了避免样式泄漏到演示者模式请尽量把样式作用域收窄到单张幻灯片或将选择器包裹在.slidev-layout之下。 :::示例请使用.slidev-layout .grid { ... }而不要直接写.grid { ... }。「尽量作用域化」的另一条正路是把样式放进幻灯片自身的style标签或使用style作用域机制相关讲解见 幻灯片作用域样式 与 区块样式block frontmatter。自定义index.html注入匹配规则项目根目录下的index.html该文件用于向 Slidev 最终生成的宿主 HTML 注入额外的head标签如外部字体、meta与body脚本。例如自定义文件见 directory-structure.mdhead link relpreconnect hrefhttps://fonts.gstatic.com link hrefhttps://fonts.googleapis.com/css2?familyFiraCode:wght400;600familyNunitoSans:wght200;400;600displayswap relstylesheet /head body script src./your-scripts/script /body合并后的最终宿主页面形如!DOCTYPE html html langen head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 link relicon typeimage/png hrefhttps://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png !-- injected head -- link relpreconnect hrefhttps://fonts.gstatic.com link hrefhttps://fonts.googleapis.com/css2?familyFiraCode:wght400;600familyNunitoSans:wght200;400;600displayswap relstylesheet /head body div idapp/div script typemodule src__ENTRY__/script !-- injected body -- script src./your-scripts/script /body /html底层实现在 indexHtml.tsSlidev 会遍历所有 root 读取各自的index.html用parseHtmlForUnheadExtraction解析head中的标签并合并进最终的 head 输出见 indexHtml.ts同时把body中的内容抽取出来追加到宿主模板的!-- body --标记处见 indexHtml.ts。由此可以总结出两条重要规则只写片段不要写完整文档若用户index.html包含!DOCTYPE声明Slidev 会直接忽略该文件并输出警告——这个文件很可能是被误放进项目的生成物见 indexHtml.tsSlidev 自身会生成title、favicon、description、Open Graph、Twitter Card、字体link等相关细节见 SEO Meta所以index.html只应补充它没有覆盖的第三方标签与脚本。全局图层文件匹配规则global-top.vue|global-bottom.vue|custom-nav-controls.vue|slide-top.vue|slide-bottom.vue这些文件用于创建跨幻灯片持续存在的图层适合页脚、全局特效、跨页动画等场景。完整用法与示例见 全局图层Global Layers其图层在 Z 轴上从顶层到底层依次为NavControls含自定义导航控件custom-nav-controls.vueGlobal Topglobal-top.vue单实例Slide Topslide-top.vue每张幻灯片一个实例Slide Content幻灯片正文Slide Bottomslide-bottom.vue每张幻灯片一个实例Global Bottomglobal-bottom.vue单实例例如在项目根目录创建global-bottom.vue!-- global-bottom.vue -- template footer classabsolute bottom-0 left-0 right-0 p-2Your Name/footer /template这段文字会出现在所有幻灯片上。结合 全局上下文$nav 还可以做条件渲染例如「隐藏第 4 页 / cover 布局的页脚」!-- 从第 4 页起隐藏页脚 -- template footer v-if$nav.currentPage ! 4 classabsolute bottom-0 left-0 right-0 p-2 Your Name /footer /template!-- 在 cover 布局中隐藏页脚 -- template footer v-if$nav.currentLayout ! cover classabsolute bottom-0 left-0 right-0 p-2 Your Name /footer /template注意若global-top.vue/global-bottom.vue依赖当前导航状态导出 PDF 时应使用--per-slide参数以确保每页状态正确或者直接改用按页生效的slide-top.vue/slide-bottom.vue。从源码 global-layers.ts 可以看到加载器会为每个 root 生成*.{ts,js,vue}的导入 glob并额外兼容一组候选命名见 global-layers.tsglobal-top/GlobalTop、global-bottom/GlobalBottom、slide-top/SlideTop、slide-bottom/SlideBottom。也就是说这些图层文件对大小写与连字符命名都是宽容的。此外模板为全局/单页图层生成了GlobalTop/GlobalBottom组件顶层为h(comp)渲染而custom-nav-controls走独立的导航控件虚拟模块二者机制相互独立。容易被忽略的两个约定目录setup/与snippets/目录树中的另外两个成员虽然没有专属小节但它们同样是目录约定的组成部分。setup/自定义 setup / hooks匹配规则./setup/hook-name.{ts,js}。Slidev 在启动时会为每个 root 解析setup/filename并把模块的default导出当作 hook 函数调用见 load.tsexport async function loadSetupsF(roots, filename, args) { return await Promise.all(roots.flatMap((root) { const path resolve(root, setup, filename) if (existsSync(path)) { tasks.push(loadModule{ default: F }(path).then(mod mod.default(...args))) } ... })) }例如仓库自身对主题/高亮、KaTeX、预解析器、Shiki transformer 等的接入就位于 node/setups。你可以通过创建setup/shiki.ts、setup/katex.ts、setup/code-runners.ts、setup/preparser.ts等文件覆盖/扩展对应能力——每类 hook 的完整配置与示例在 docs/custom 目录下有对应专题如 config-highlighter.md、config-katex.md、config-code-runners.md、config-parser.md 等查阅 目录索引 可快速定位全部可用 hook。snippets/代码片段匹配规则./snippets/*。这里的文件会被 Shiki 的代码块导入语法按需读取例如在代码块内使用 /snippets/foo.ts/指向项目根把外部文件内容拉进幻灯片并做语法高亮可配合#region选取指定片段或使用行号范围 /snippets/external.ts#snippet ts /snippets/snippet.ts#snippet ts这种写法在仓库的 magic-move 集成测试中直接可见见 magic-move.test.ts说明片段导入在语法转换管线中是先于代码块解析执行的且修改片段源文件会触发对应幻灯片的增量更新。完整语法请参考 代码片段导入。参考一个最小真实项目形态仓库的 demo/starter 就是这套约定的最小实现包含components/Counter.vue、pages/imported-slides.md分页导入、snippets/external.ts、根级style.css与vite.config.ts目录里甚至没有layouts/、setup/、public/与index.html——再次印证了「所有约定项均可选、按需声明」的设计取向。当你开始自己的演讲项目时建议同样遵循「先放slides.md跑通再按需增量加入上述目录」的路径某一天你想放个全局页脚就新建global-bottom.vue想让代码引用外部文件就新建snippets/想统一风格就新建styles/——每一处都是位置即语义不需要任何额外配置。【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表