如何编写一个 Valaxy 主题
提示
Valaxy 与 Vite/Vue 的生态完全兼容,因此你在编写主题时,可以任意使用第三方的 Vite/Vue 插件。
Valaxy 主题无需预编译,直接发布源文件即可。
撰写中…
作为 Valaxy 作者,我可以很轻松的实现自己的主题。 但也因此,我可能很难了解真正主题开发者的需求。
因此,如果你有任何开发主题的相关问题, 可前往 QQ 频道「云乐坊」 或发起 Discussions 与我交流,我将会为您提供尽可能的帮助,并针对泛化的问题撰写文档。
对了,由于目前的主题并不多,主题作者可以在这里发现一些来自云游君私人的奖励。
主题示例
- valaxy-theme-starter: Valaxy 主题开发模版
- valaxy-theme-yun: valaxy-theme-yun 一个更完善的主题示例
- valaxy-theme-press: valaxy-theme-press 当前文档主题示例
创建主题模板
在动手之前,我们先来了解一下一个 Valaxy 主题的基础结构,它与正常的用户目录结构也十分相似。
以 valaxy-theme-yun 为例:
尽管它们看起来很多,但是大部分都是可选的,你可以根据主题的需求按需编写。
App.vue: 主题的入口文件,用于挂载全局的主题组件README.md: 主题的说明文档(毫无疑问,这是必不可少的 😛)client:主题所暴露给用户的客户端辅助函数index.ts: 主题的客户端辅助函数入口文件
components: 主题的组件ValaxyMain.vue: 主题的文章渲染组件YunSidebar.vue: 主题的侧边栏组件YunSponsor.vue: 主题的赞助组件YunWaline.vue: 第三方评论 Waline 适配组件
composables: 辅助的 Composition APIconfig.ts: 主题的配置文件helper.ts: 主题的辅助函数index.ts: 主题的 Composition API 入口文件post.ts: 主题的文章相关的辅助函数
docs: 主题的文档(自由用你喜欢的结构组织并展示吧!)出于定制化与 DogFooding 的考虑,Valaxy 的文档采用自身制作,并制作了一个文档主题 valaxy-theme-press,如果你只是想要一个简单轻量的文档站点,Vitepress 是个不错的选择。(valaxy-theme-starter 在未来也许会内置该示例模版。)
en-US: 英文文档zh-CN: 中文文档
features: 主题特色功能,一些不依赖于 Vue Composition API 的功能(区别于composables)fireworks.ts: 烟花点击效果
layouts: 主题的布局(扩展更多布局)default.vue: 默认布局home.vue: 首页布局layout.vue: 文章列表布局post.vue: 文章布局(放置于pages/posts/文件夹下的文章默认为 `post 布局)tags.vue: 标签布局
locales: 主题的多语言支持en.yml: 英文语言文件zh-CN.yml: 中文语言文件
node_modules: 主题的依赖(请勿提交至仓库)node: 主题的 Node 端逻辑package.json: 主题的相关信息与依赖pages: 主题的默认页面(扩展更多页面)index.vue: 首页page: 普通页[page].vue: 文章列表页,动态路由,如/page/2
setup: 主题的入口文件(可注册 Vue 插件等)main.ts: 主入口文件defineAppSetup
stores: 主题的状态管理app.ts: 全局状态管理文件
styles: 主题的样式index.ts: 主题的样式入口文件
tsconfig.json: 主题的 TypeScript 配置types: 主题的类型声明index.d.ts: 主题的类型声明入口文件
unocss.config.ts: 主题的 unocss 配置utils: 主题的工具函数valaxy.config.ts: 主题的配置文件
APIs
我们提供了一个扩展函数,以供你快速扩展页面信息。
你也可以直接扩展 vue-router/vite 插件中的 extendRoute。
https://github.com/posva/unplugin-vue-router/issues/43#issuecomment-1433140464 (now part of vue-router)
提示
data 解析自 Markdown frontmatter,为原始数据(不可变),将会被合并至 route.meta.frontmatter 中。
Client
切换亮暗模式
以下变量被存储在全局状态中,你可以通过 useAppStore 获取。
isDark: 是否启用了暗黑模式themeColor: 主题色(可跟随 isDark 变化)toggleDark: 切换暗黑模式toggleDarkWithTransition: 带有过渡效果的切换暗黑模式
你可以通过
themeConfig.valaxyDarkOptions来配置暗黑模式的相关选项。
Default Theme Config.valaxyDarkOptions
Node
Hooks
开始编写
App.vue
你的入口文件
譬如我想要为主题添加一个全局的 Loading 页面。
你可以从 valaxy 导入全局状态 useAppStore,记录 showLoading 来实现。
你也可以使用你自己的全局状态管理。参见 全局状态管理。
提示
- 你可以通过
ValaxyApp.vue组件完全覆盖根组件,来达成你更深层次的定制化需求。(完全由你自定义,不再默认挂在router-view等默认处理。)
ValaxyMain
你需要自定义一个 ValaxyMain 组件来决定主题的文章渲染部分。
你可以从
ValaxyMain的props中获取frontmatter与pageData。
样式
引入默认样式
Valaxy 提供了一些默认样式,你需要在主题中自行引入。
例如,新建 valaxy-theme-yun/setup/main.ts:
Markdown 样式
Markdown 样式是主题呈现文章样式的部分,需要由主题自定义。
你可以参考 valaxy-theme-press 自定义 Markdown 主题的方式,见 styles/markdown.scss。
如果你想先使用常见的默认样式(后续再进行定制),你可以直接使用 star-markdown-css。 使用方式可参见 valaxy-theme-yun/styles
NProgress 进度条
内置了基础的 nprogress 样式,你可以通过覆盖 nprogress 的默认样式进行定制:
功能
API
你还可以使用 Valaxy 内置的 API 以快速实现相关功能。
获取用户的 Valaxy Config
你可以通过内置的 useValaxyConfig 获取用户的 Valaxy 配置。
提示
这部分配置与用户的 valaxy.config.ts 中的配置相对应,但它仅在客户端使用,因此并不包含 Node 端相关配置(如 vite 等)。
提供 Typed useThemeConfig
你可以提供一个主题的 useThemeConfig 函数,以便自己/用户获得带有类型约束的配置。
获取文章列表
获取文章列表有两种方式。
usePostList: 获取文章列表(不推荐)
useSiteStore: 获取全局站点信息(推荐)
以上两者之间的区别是,usePostList 是一个基础函数,每次调用都会获取所有文章并重新过滤一次,而 useSiteStore 则会先调用 usePostList 并将获取的文章列表缓存在全局的状态中,以供你后续调用。
(此外,useSiteStore 还实现了保存文章时(如标题)热更新列表信息的功能。)
valaxy/packages/valaxy-theme-yun/components/YunPostList.vue 是一个使用
useSiteStore展示文章列表的示例。 分页功能可参考 valaxy-theme-yun/pages/page/[page].vue 与 valaxy-theme-yun/components/YunPostList.vue。
获取文章分类与标签
在你获取文章列表后,site.postList 中的每篇文章都具有 categories(分类) 与 tags(标签) 属性。
你还可以通过 useCategories 与 useTags 获取所有分类、标签,其中便包含了与文章的对应关系。
- valaxy/packages/valaxy-theme-yun/layouts/categories.vue 是一个使用
useCategories展示文章分类的示例。 - valaxy/packages/valaxy-theme-yun/layouts/tags.vue 是一个使用
useTags展示文章标签的示例。(useYunTags是主题对useTags的封裝。)
useTags中的tags为一个对象,其键为标签名,值为对应的文章列表。useCategories可传入参数category(useCategories('aaa')) 以获取指定分类的文章列表。
获取 Front-matter
你可以通过 useFrontmatter 获取当前页面的 Front-matter。
譬如:
全局状态管理
你可以借助 Pinia (Valaxy 内置)建立自己的全局状态,并在随后使用它,
上一篇/下一篇
文章底部通常存在切换上一篇/下一篇的导航。
你可以利用 siteStore.postList 自行实现,也可以使用 Valaxy 提供的 usePrevNext。
目录
如果你想要快速实现一个目录,Valaxy 提供了一个内置钩子函数 useOutline。
你可以用它快速获取文章页的目录信息 headers 与对应点击事件 handleClick,如:
引用静态资源
当主题需要内置一些静态资源(如:图片等),你可以通过相对引用的方式实现。(这在 scss 样式文件中也适用)
譬如 assets 与 components 处于同一目录下时:
Third Plugin
实现评论
作为博客,用户通常会有评论的需求。
而由于评论系统各不相同,如 Hexo 等主题开发者们通常需在主题侧重复实现多款评论系统。 这显然是繁琐的。
Valaxy 决定通过插件中心化地提供各类封装好的评论组件和辅助函数。
譬如主题开发者,可以借助 valaxy-addon-waline 来快速实现 Waline 评论系统的集成。 而用户则可以使用相同的配置穿梭漫游于不同的主题之间。
集成参见 valaxy-addon-waline。
性能优化
添加依赖预构建 optimizeDeps
为了提高后续页面的加载性能,Vite 将那些具有许多内部模块的 ESM 依赖项转换为单个模块。 如果你的主题依赖了一些大型的 ESM 包,你可以通过添加 optimizeDeps 选项来预构建这些依赖项。
dayjs已被默认预构建,您无需再次添加。 为什么用 dayjs 而不是 date-fns?
提醒特殊需求的用户安装第三方插件
如果您的主题适配了多个 addon,但用户并非都需要安装。 如评论插件:
valaxy-addon-walinevalaxy-addon-twikoo
当用户没有主动安装对应 addon 时(即 addon 不存在的情况),则会默认重定向至一个空函数。
因此,如果某个插件不是必须的,请在主题文档中提醒想要使用该功能的用户安装对应插件。
To Be Continued.