跳到主要内容

小程序多主题方案

自由的 web 方案

对于 web 来说,多主题色的需求是非常常见的,比如 暗黑模式 就是一个极其常见的需求,

web 上的解决方案无非就是,通过动态切换 css 变量的值达成效果,或者通过 .dark / [data-theme] 选择器,包裹暗黑模式下页面和组件的样式,通过增加选择器的优先级,来覆盖默认的样式等等...

那么小程序的方案应该怎么去实现呢?

目前小程序存在的限制

首先,小程序中是没有 :root/html 选择器的,取而代之的是 page 标签选择器,因为小程序中 page 才是每个页面的根节点。

其次小程序本身都是多页面的,而我们经常写的 vue/react 等等spa应用都是单页应用(可以做多页,我只是举个大多数的情况,不要钻牛角尖哈)。

而且 web 中我们可以通过 element.style.setProperty 这样的 js api 轻而易举的修改 css 变量的值,但是小程序不行,

我们以微信小程序中选取 wxmlapi: wx.createSelectorQuery 为例,它甚至都无法选中 page 标签,更何况即使选中了,目前也没有能力动态的通过 js api 的方式去设置css 变量的值。

那么我们究竟应该怎么设计方案呢?

方案的设计和实现

设计思路

首先既然我们无法利用节点的变量切换来达成效果,但是我们可以通过组件的特性,即数据的响应式和插槽来达成效果。

我们可以设计一个 ConfigProvider 组件,它拥有一个dom节点,内部是一个插槽

其中那个dom节点就是我们主题相关变量寄居在的节点,而这个组件往往会作为一个根组件,在每个页面中被使用,去包裹我们真正的业务页面

甚至我们可以再设计一个 BaseLayout 这样的组件,去包含每个页面公共的部分,再在其中去引用 ConfigProvider,然后做一层插槽的透传即可。

现在让我们即可开始动手吧!

实现

这里我以 vue 的语法作为示例,因为我个人认为它比 react原生 更容易让新手看懂

ConfigProvider的实现:

<template>
<view :class="[mode]" :style="styleObj">
<slot></slot>
</view>
</template>

<script lang="ts">
import { defineComponent, computed, PropType } from 'vue'
// import store from '@/store'
export default defineComponent({
props: {
vars: {
type: [Object]
},
mode: {
type: [String] as PropType<'light' | 'dark'>,
default: 'light' // 这里你可以使用 store.state.mode 这样的值来获取用户的配置
}
},
setup(props) {
const styleObj = computed(() => {
return Object.assign({}, props.vars)
})
return {
styleObj
}
}
})
</script>

其中,mode 这个 prop 用来模拟实现了 <html data-theme="<theme>" class="<theme>"></html> 的效果,而 vars 则用来模拟实现 js api 设置 css 变量的效果。

通过这 2props,你既可以通过 mode 的切换,把多个主题以及对应的变量值全部给写在你自己的 css中,然后通过切换mode,触发样式的覆盖来切换主题,这种是为静态的切换。

又可以通过设置 vars的值去动态的覆盖和切换,比如从服务端获取css变量的值,然后set进组件中,这显然是非常灵活的,这种是为动态的切换。

现在有了这个组件,我们就可以用它去包裹每一个页面了。

然后下一步,自然是要我们的页面和组件,都去应用那些我们设计的 css 变量了。

这一块可以参考下方链接中的动态调整系统主题色(4)中的CssVar方案,里面也有和 tailwindcss 相结合的部分,也欢迎阅读动态调整web系统主题 系列文章,并与在下进行探讨。

动态调整主题参考链接

  1. 动态调整web系统主题? 看这一篇就够了
  2. 动态调整web主题(2) 萃取篇
  3. 动态调整web主题(3): 基于tailwindcss插件的主题色生成方案
  4. 动态调整系统主题色(4): CssVar 与 Variant 方案的探索

参考示例

微信上搜索 tailwind(未交 30 元个人资质费用,已无法搜索),进入小程序即可,小程序码:

tailwind

实现源代码详见: weapp-tailwindcss/tailwindcss-weapp