颜清华 发表于 2025-8-5 09:00:26

duxapp中主题系统是如何实现动态切换的

在旧版本的duxapp,支持主题功能,但是那时候的主体是静态配置的,并不支持动态切换,新版本,在旧的静态主题基础上扩展,实现了动态主题切换
旧版本静态主题

在之前的版本中已经支持主题功能,在用户配置用,使用模块的 theme 字段配置主题,像下面这样
// configs/config/index.js
option: {
// 基础模块
duxapp: {
    theme: {
      primaryColor: '#CDDE00',
      secondaryColor: '#FDD000',
      successColor: '#34a853',
      warningColor: '#fbbc05',
      dangerColor: '#ea4335',
      pageColor: '#fafbf8'
    }
}
}配置了这些主题参数,会通过一个脚本转化为scss变量被加入到全局scss变量中,就像下面这样
$duxappPrimaryColor: #CDDE00;
$duxappSecondaryColor: #FDD000;
$duxappSuccessColor: #34a853;
$duxappDangerColor: #ea4335;
$duxappWarningColor: #fbbc05;
$duxappPageColor: #fafbf8;然后你就能在任何scss文件中调用这些变量,例如 duxappStyle 的全局scss中调用这些变量,当然也不局限于这个文件,任何的scss都能调用这些变量
// src/duxappStyle/app.scss
.bg-primary {
background-color: $duxappPrimaryColor;
}

.bg-secondary {
background-color: $duxappSecondaryColor;
}

.bg-success {
background-color: $duxappSuccessColor;
}然后在你的项目中就可以调用这些全局类名就能获得对应的样式
如果要编写 style 的时候获取这些主题参数,可以像下面这样
import { duxappTheme } from '@/duxapp'

<View style={{ backgroundColor: duxappTheme.primaryColor }} />

[*]关于如何定制自己模块的主题,查看这个文档
新版本的动态主题

为了最小的升级成本,系统未对之前的主题系统进行大改,而是在当前的主题系统模式上进行简单调整就能使用
之前所有的内容在新的主题系统中都是生效的,如果你要切换到动态主题,只需要在用户配置中,配置多套主题即可

[*]将当前的模块配置中的 theme 移动到 themes.light (每个模块的配置都需要同样的操作)
[*]在 themes 里面新增一个主题配置 dark,表示夜间模式的主题
:::info
[*]如果没有指定 themeConfig.default light将会作为默认主题,
[*]在你配置 dark 的主题的时候,如果和默认配置 light 相同的配置,你可以不配置,只需要与 light 不同的部分即可
:::
[*]新增 themeConfig 配置项目,参考下面的示例,需要在里面配置主题列表 themes (必须配置) 其他三个选项为可选配配置,dark 用于指定页面模式主题,light 用于指定白天模式主题,default 指定默认主题
[*]只有当夜间模式和白天模式两个主题都存在的情况下系统才会跟随系统主题进行系统切换
[*]除了 light dark 你还可以配置更多的主题,通过 theme.setMode(主题) 切换
const config = {
option: {
    // 基础模块
    duxapp: {
      themeConfig: {
      themes: {
          light: {
            name: '明亮主题',
            color: '#fff'
          },
          dark: {
            name: '暗黑主题',
            color: '#333'
          }
      },
      // dark: 'dark',
      // light: 'light',
      // default: 'light'
      },
      themes: {
      light: {
          primaryColor: '#E70012',
          secondaryColor: '#0092e8',
          successColor: '#34a853',
          warningColor: '#fbbc05',
          dangerColor: '#ea4335',
          pageColor: '#F7F9FC',

          textColor1: '#373D52',
          textColor2: '#73778E',
          textColor3: '#A1A6B6',
          textColor4: '#FFF',
          header: {
            color: '#fff', // 仅支持rgb hex值,请勿使用纯单词 设置为数组将显示一个渐变按钮
            textColor: '#000', // 文本颜色
            showWechat: true, // 微信公众号是否显示header
            showWap: true, // h5是否显示header
          }
      },
      dark: {
          pageColor: '#1E1E1E',

          whiteColor: '#181818',
          blackColor: '#fff',
          lineColor: '#1F1F1F',

          textColor1: '#FFF',
          textColor2: '#A1A6B6',
          textColor3: '#73778E',
          textColor4: '#373D52',
          header: {
            color: '#121212',
            textColor: '#fff'
          },
          loading: {
            dark: '#fff',
            blank: '#7a7a7a'
          }
      }
      }
    },
    duxui: {
      themes: {
      light: {
          button: {
            radiusType: 'round'
          }
      },
      dark: {
          tabBar: {
            nameColor: '#888',
            nameHoverColor: '#fff'
          }
      }
      }
    }
}
}

export default config动态切换

默认情况下只需要你配置了 light 和 dark 两个主题,程序就能跟随系统自动切换
如果你需要手动切换,下面是一动态切换主题的示例代码,参考这个进行开发,theme 是基础模块导出的工具
:::info

[*]动态切换(包括自动切换)现在仅支持 小程序 H5端,其他平台还在开发中
[*]不支持的平台会按照配置的默认主题显示
:::
import { Header, ScrollView, TopView, GroupList, theme, Button } from '@/duxuiExample'

export default function ThemeExample() {

const mode = theme.useMode(true)

const modes = theme.useModes()

return <TopView>
    <Header title='Theme' />
    <ScrollView>
      <GroupList>
      <GroupList.Item title='主题切换功能' desc='主题切换当前仅支持小程序和H5端,其他端还在努力开发中'
          className='gap-3'
      >
          {
            modes.map(item => <Button
            type='primary'
            plain={item.mode !== mode}
            key={item.name}
            onClick={() => item.switch()}
            size='l'
            >{item.name}</Button>)
          }
      </GroupList.Item>
      </GroupList>
    </ScrollView>
</TopView>
}如何实现的

动态主题在不同的平台使用了不同的实现方案,具体来说,小程序 H5端使用了css变量,RN端使用了插件动态修改组件代码实现动态切换
小程序 H5端

小程序 H5端 css 变量 的实现流程

[*]duxapp-cli 先将用户配置的主题名称进行统计并存储
[*]duxapp-cli 根据用户主题配置生成主题scss文件 src/duxapp/userTheme/index.scss,这个文件会自动被 TopView组件引用
[*]编写一个 theme-loader 将其插入到 webpack 的 loader 中,在处理 scss 之前,先解析动态主题,目的是将调用到scss主题变量的代码,替换为调用css变量
[*]主题系统通过一个改变类型动态切换css变量的流程
RN端


[*]duxapp-cli 先将用户配置的主题名称进行统计并存储
[*]duxapp-cli 根据用户主题配置生成主题js文件 src/duxapp/userTheme/index.rn.js
[*]编写一个 theme-loader 将其插入到 webpack 的 loader 中,在处理 scss 之前,先解析动态主题,目的是将调用到scss主题变量的代码,替换为调用css变量
[*]使用 patch-package 修改了 rn 端相关插件,主要改变的功能是,解析 theme-loader 生成的css变量,并替换成主题变量,并且将之前的 styleSheet 修改为函数的方式导出,函数接受一个主题名称,传入不同的主题名称,返回不同的 styleSheet
[*]修改插件,让插件在函数组件的头部,插入一个 hook ,这个 hook 会监听主题改变,并更新 styleSheet 并且在主题更新后,重新渲染组件
因为 React 组件的编写形式太过灵活多变,因此最后一步,在函数组件头部插入一个hook的操作,并不一定所有组件都能插入,局具体使用的显示参考这个说明
最后

duxapp的主题系统经历了从静态配置到动态切换的演进过程:

[*]兼容性:新版本完美兼容旧版的静态主题配置
[*]灵活性:支持多主题配置和动态切换
[*]跨平台:针对不同平台采用最优实现方案
[*]易用性:提供简洁的API和配置方式
开发者可以根据项目需求选择合适的主题方案,通过简单的配置即可实现强大的主题功能。未来版本将继续优化各平台的兼容性和性能表现。
主题开发文档
开发文档
GitHub

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: duxapp中主题系统是如何实现动态切换的