Stories

Detail Return Return

duxapp中主題系統是如何實現動態切換的 - Stories Detail

在舊版本的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;
}

然後在你的項目中就可以調用這些全局類名就能獲得對應的樣式

<View className='bg-primary' />

如果要編寫 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

動態切換

默認情況下只需要你配置了 lightdark 兩個主題,程序就能跟隨系統自動切換

如果你需要手動切換,下面是一動態切換主題的示例代碼,參考這個進行開發,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

user avatar wxp686 Avatar dunizb Avatar kanshouji Avatar zhedan_sam_wan9 Avatar pengxiaohei Avatar munergs Avatar faurewu Avatar xiangzhihong Avatar panpanpanya Avatar shenyongweiwudemaozi Avatar lovehx_58bc3f5518bf4 Avatar ailvyoudetiebanshao Avatar
Favorites 14 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.