前言

最近项目中需要接入用户协议与隐私政策,法务部门提供的是 Word 文档。为了方便维护和在项目中直接渲染,我将文档转换为 Markdown 格式,并通过 Vite 的 unplugin-vue-markdown 插件将其作为 Vue 组件直接引入:

<template>
  <Privacy />
</template>

<script lang="ts" setup>
import Privacy from "@/locales-other/zh-CN/agreements/privacy.md";
</script>

内容渲染所在的容器通常带有一个 markdown-body 的类名。由于浏览器默认样式较为简陋,我决定引入 github-markdown-css 来复刻 GitHub 优雅的阅读体验。

首先安装依赖:

pnpm i github-markdown-css

然而,在对接项目现有的亮/暗色模式切换机制时,我遇到了一些样式冲突的问题。本文将分享如何优雅地解决这一问题。

项目中的暗色模式实现机制

在介绍解决方案前,先简要说明项目中现有的主题切换方案。通常我们使用 vueuse 库中的 useDarkuseToggle 来管理:

import { useDark, useToggle } from "@vueuse/core";

const isDark = useDark({
  valueDark: "dark",
  valueLight: "light"
});
const toggleDark = useToggle(isDark);

为了方便全局调用,这部分逻辑通常封装在 Pinia Store 中。该方案会自动监听系统主题变化,或通过用户手动切换,在 html 标签上动态添加或移除 dark 类名:

<html class="dark">
  ...
</html>

配合 CSS 变量,我们可以轻松实现主题切换:

:root {
  --bg: #fff;
}

:root.dark {
  --bg: #000;
}

body {
  background-color: var(--bg);
}

遇到的问题:样式适配冲突

在引入 github-markdown-css 时,最直接的方式是在 main.ts 中全局引入:

import "github-markdown-css";

问题在于:默认引入的样式文件是通过 CSS 媒体查询 @media (prefers-color-scheme: dark) 来适配系统主题的,它并不识别我们在 html 标签上添加的 .dark 类名。这导致当用户在项目中手动切换主题时,Markdown 的样式无法随之切换。

解决方案:动态加载样式文件

为了解决上述冲突,我们需要根据当前的模式(Light/Dark)动态加载对应的 CSS 文件。

github-markdown-css 5.0 及以上版本中,作者提供了独立的文件:github-markdown-light.cssgithub-markdown-dark.css。需要注意的是,这两个文件不能同时引入,否则样式规则会发生冲突。

因此,最佳实践是使用 <link> 标签引入样式,并根据主题状态动态修改其 href 属性。在 Vue 项目中,我们可以利用 @unhead/vue 来优雅地管理头部标签。

题外话:如果你为了更好的响应体验,我觉得可以使用两个link标签,分别对应亮色和暗色模式,然后通过 disabled 属性来控制启用状态。不过本文不展开讨论这一点。

1. 安装依赖

pnpm i @unhead/vue

2. 实现动态切换逻辑

我们可以在根组件 App.vue 或布局组件中编写如下逻辑。利用 Vite 的 ?url 后缀,我们可以获取 CSS 文件的静态资源路径,而不是将其作为样式直接注入到 JS bundle 中。

<template>
  <router-view />
</template>

<script lang="ts" setup>
import { useHead } from "@unhead/vue";
import { useAppStore } from "@/stores";
// 显式获取 CSS 文件的 URL 路径
import markdownLightUrl from "github-markdown-css/github-markdown-light.css?url";
import markdownDarkUrl from "github-markdown-css/github-markdown-dark.css?url";

const appStore = useAppStore();

useHead({
  link: [
    {
      key: "markdown-theme", // 设置 key 以确保 link 标签会被替换而不是追加
      rel: "stylesheet",
      href: () => (appStore.isDark ? markdownDarkUrl : markdownLightUrl)
    }
  ]
});
</script>

总结

通过 ?url 引入资源路径配合 useHead 的响应式处理,我们成功实现了 github-markdown-css 样式跟随项目主题的丝滑切换。这种方式既保留了 GitHub 的原生样式体验,又完美兼容了基于类名的暗色模式方案。

分类: vue 项目实战 标签: 主题github-markdown-css亮色暗色

评论

暂无评论数据

暂无评论数据

目录