react.js|react-vant 实现黑暗模式

使用 root css 变量实现黑夜模式 做起来很简单,首先,我们在根组件里拿到是否为黑暗模式的变量(我这里是 darkTheme),并将其挂载到 html 节点的 data-theme 属性上。

const MyApp = function (props) { const { darkTheme } = useUserProfile()useEffect(() => { // 注意这里 document.documentElement.setAttribute('data-theme', darkTheme ? 'dark' : '') }, [darkTheme])return ( ... ) }

然后在全局 css 样式里配置如下内容:
/* 黑夜模式的变量配置 */ :root[data-theme="dark"] { --rv-white: #1E293B; --rv-black: #9CA3AF; --rv-blue: #8589ff; --rv-gray-1: #0F172A; --rv-gray-2: #3C4A60; --rv-gray-7: var(--rv-black); }

这样,当 darkTheme 为 true 时,css 里配置的黑夜模式变量就会覆盖原先的 root 变量。
坑:不要在 dark class 样式类下覆盖 root 变量 有些同学可能会习惯性的这么写:
const MyApp = function (props) { const { darkTheme } = useUserProfile()// 根据 dark 类判断是否为黑夜模式 return ( ... ) }

然后在 .dark 样式里写黑夜变量:
.dark { --rv-white: #1E293B; --rv-black: #9CA3AF; /* ... */ }

然后就会出现下面这个诡异的 bug,可以看到从 devtool 里链接过去的 --rv-white 确实是黑色,但是引用这个变量的 css 样式却仍然是白色。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXp0Gb0I-1654827759454)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fcd514a547a04e67913825a670bad6d9~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
导致这个问题的原因是 --rv-cell-background-color 的层级(:root)比 .dark 样式类的层级要高,就导致 --rv-cell-background-color 在向上寻找 --rv-white 的值时没有找到我们定义的 .dark 样式变量,而是找到了和他同级的 --rv-white: #fff
react.js|react-vant 实现黑暗模式
文章图片

所以说,如果想用 .dark 类的话,需要和上文中提到的一样,通过给 html 根节点来设置样式类来实现。
坑2:DatePicker 的渐变遮罩修改 在完成了大部分黑暗模式适配时,你可能会发现你的 DatePicker 变成这样子了:
react.js|react-vant 实现黑暗模式
文章图片

打开 devtool 一看,发现它的遮罩是直接写死的,不能通过 css 变量配置:
react.js|react-vant 实现黑暗模式
文章图片

要修改起来其实也不难,跟其他黑暗模式的代码写一起就可以了:
:root[data-theme="dark"] .rv-picker__mask { background-image: linear-gradient(180deg, hsl(217deg 33% 17% / 90%), hsl(217deg 33% 17% / 40%)), linear-gradient(0deg, hsl(217deg 33% 17% / 90%), hsl(217deg 33% 17% / 40%)); }

再看下,恢复正常:
react.js|react-vant 实现黑暗模式
文章图片

为什么不用 ConfigProvider 【react.js|react-vant 实现黑暗模式】至于为什么不适用 vant 提供的 ConfigProvider 来设置样式,有这么两个原因:
  • 配置数量过多
官方文档中提到 ConfigProvider 只能用来修改组件变量。
react.js|react-vant 实现黑暗模式
文章图片

所以说如果用这种方法的话,要配置的变量就会很多,下面是我曾经进行的修改,注意,这些也只修改了一半左右,和上面使用 root 变量覆盖只需要五六行就完成了所有修改简直一个天上一个地下:
react.js|react-vant 实现黑暗模式
文章图片

  • 需要适配弹出框节点
react 应用一般都挂载到 body 元素下的一个 div 里,而应用里的弹出组件,例如 DialogActionSheetPopup 等同样是挂载到 body 下的,和我们的 app 根节点同级,这就导致了这些弹出组件实际上不是 ConfigProvider 的子节点。
也就是说,我们需要找到应用里每一个弹出组件,给它设置好 teleport 才行。下面是官方文档的说明:
react.js|react-vant 实现黑暗模式
文章图片

如果你的应用已经积累了很多代码了的话,这么搞会是非常痛苦的一件事。
tailwind 适配 如果你项目里使用了 tailwind 的话,可以用下面这种方式来复用刚刚我们在 react-vant 的实现的黑暗模式配置,在 tailwind.config.js 里的 theme.extend.colors 里引用对应的颜色即可:
module.exports = { content: [ // ... ], theme: { extend: { colors: { mainColor: "var(--rv-black)", cardBackground: "var(--rv-white)", background: "var(--rv-gray-1)" }, }, }, // ... }

注意,要写在 extend 里,如果你直接写在 theme.colors 里会发现其他的 color 都无效了。
之后当作类名用即可:

    推荐阅读