duxapp中主題系統(tǒng)是如何實現(xiàn)動態(tài)切換的

在舊版本的duxapp,支持主題功能,但是那時候的主體是靜態(tài)配置的,并不支持動態(tài)切換,新版本,在舊的靜態(tài)主題基礎(chǔ)上擴(kuò)展,實現(xiàn)了動態(tài)主題切換

舊版本靜態(tài)主題

在之前的版本中已經(jīng)支持主題功能,在用戶配置用,使用模塊的 theme 字段配置主題,像下面這樣

// configs/config/index.js
option: {
  // 基礎(chǔ)模塊
  duxapp: {
    theme: {
      primaryColor: '#CDDE00',
      secondaryColor: '#FDD000',
      successColor: '#34a853',
      warningColor: '#fbbc05',
      dangerColor: '#ea4335',
      pageColor: '#fafbf8'
    }
  }
}

配置了這些主題參數(shù),會通過一個腳本轉(zhuǎn)化為scss變量被加入到全局scss變量中,就像下面這樣

$duxappPrimaryColor: #CDDE00;
$duxappSecondaryColor: #FDD000;
$duxappSuccessColor: #34a853;
$duxappDangerColor: #ea4335;
$duxappWarningColor: #fbbc05;
$duxappPageColor: #fafbf8;

然后你就能在任何scss文件中調(diào)用這些變量,例如 duxappStyle 的全局scss中調(diào)用這些變量,當(dāng)然也不局限于這個文件,任何的scss都能調(diào)用這些變量

// src/duxappStyle/app.scss
.bg-primary {
  background-color: $duxappPrimaryColor;
}

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

.bg-success {
  background-color: $duxappSuccessColor;
}

然后在你的項目中就可以調(diào)用這些全局類名就能獲得對應(yīng)的樣式

<View className='bg-primary' />

如果要編寫 style 的時候獲取這些主題參數(shù),可以像下面這樣

import { duxappTheme } from '@/duxapp'

<View style={{ backgroundColor: duxappTheme.primaryColor }} />
  • 關(guān)于如何定制自己模塊的主題,查看這個文檔

新版本的動態(tài)主題

為了最小的升級成本,系統(tǒng)未對之前的主題系統(tǒng)進(jìn)行大改,而是在當(dāng)前的主題系統(tǒng)模式上進(jìn)行簡單調(diào)整就能使用

之前所有的內(nèi)容在新的主題系統(tǒng)中都是生效的,如果你要切換到動態(tài)主題,只需要在用戶配置中,配置多套主題即可

  • 將當(dāng)前的模塊配置中的 theme 移動到 themes.light (每個模塊的配置都需要同樣的操作)
  • themes 里面新增一個主題配置 dark,表示夜間模式的主題
    :::info
  • 如果沒有指定 themeConfig.default light將會作為默認(rèn)主題,
  • 在你配置 dark 的主題的時候,如果和默認(rèn)配置 light 相同的配置,你可以不配置,只需要與 light 不同的部分即可
    :::
  • 新增 themeConfig 配置項目,參考下面的示例,需要在里面配置主題列表 themes (必須配置) 其他三個選項為可選配配置,dark 用于指定頁面模式主題,light 用于指定白天模式主題,default 指定默認(rèn)主題
  • 只有當(dāng)夜間模式和白天模式兩個主題都存在的情況下系統(tǒng)才會跟隨系統(tǒng)主題進(jìn)行系統(tǒng)切換
  • 除了 light dark 你還可以配置更多的主題,通過 theme.setMode(主題) 切換
const config = {
  option: {
    // 基礎(chǔ)模塊
    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值,請勿使用純單詞 設(shè)置為數(shù)組將顯示一個漸變按鈕
            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

動態(tài)切換

默認(rèn)情況下只需要你配置了 lightdark 兩個主題,程序就能跟隨系統(tǒng)自動切換

如果你需要手動切換,下面是一動態(tài)切換主題的示例代碼,參考這個進(jìn)行開發(fā),theme 是基礎(chǔ)模塊導(dǎo)出的工具

:::info

  • 動態(tài)切換(包括自動切換)現(xiàn)在僅支持 小程序 H5端,其他平臺還在開發(fā)中
  • 不支持的平臺會按照配置的默認(rèn)主題顯示
    :::
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='主題切換當(dāng)前僅支持小程序和H5端,其他端還在努力開發(fā)中'
          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>
}

如何實現(xiàn)的

動態(tài)主題在不同的平臺使用了不同的實現(xiàn)方案,具體來說,小程序 H5端使用了css變量,RN端使用了插件動態(tài)修改組件代碼實現(xiàn)動態(tài)切換

小程序 H5端

小程序 H5端 css 變量 的實現(xiàn)流程

  • duxapp-cli 先將用戶配置的主題名稱進(jìn)行統(tǒng)計并存儲
  • duxapp-cli 根據(jù)用戶主題配置生成主題scss文件 src/duxapp/userTheme/index.scss,這個文件會自動被 TopView組件引用
  • 編寫一個 theme-loader 將其插入到 webpack 的 loader 中,在處理 scss 之前,先解析動態(tài)主題,目的是將調(diào)用到scss主題變量的代碼,替換為調(diào)用css變量
  • 主題系統(tǒng)通過一個改變類型動態(tài)切換css變量的流程

RN端

  • duxapp-cli 先將用戶配置的主題名稱進(jìn)行統(tǒng)計并存儲
  • duxapp-cli 根據(jù)用戶主題配置生成主題js文件 src/duxapp/userTheme/index.rn.js
  • 編寫一個 theme-loader 將其插入到 webpack 的 loader 中,在處理 scss 之前,先解析動態(tài)主題,目的是將調(diào)用到scss主題變量的代碼,替換為調(diào)用css變量
  • 使用 patch-package 修改了 rn 端相關(guān)插件,主要改變的功能是,解析 theme-loader 生成的css變量,并替換成主題變量,并且將之前的 styleSheet 修改為函數(shù)的方式導(dǎo)出,函數(shù)接受一個主題名稱,傳入不同的主題名稱,返回不同的 styleSheet
  • 修改插件,讓插件在函數(shù)組件的頭部,插入一個 hook ,這個 hook 會監(jiān)聽主題改變,并更新 styleSheet 并且在主題更新后,重新渲染組件

因為 React 組件的編寫形式太過靈活多變,因此最后一步,在函數(shù)組件頭部插入一個hook的操作,并不一定所有組件都能插入,局具體使用的顯示參考這個說明

最后

duxapp的主題系統(tǒng)經(jīng)歷了從靜態(tài)配置到動態(tài)切換的演進(jìn)過程:

  • 兼容性:新版本完美兼容舊版的靜態(tài)主題配置

  • 靈活性:支持多主題配置和動態(tài)切換

  • 跨平臺:針對不同平臺采用最優(yōu)實現(xiàn)方案

  • 易用性:提供簡潔的API和配置方式

開發(fā)者可以根據(jù)項目需求選擇合適的主題方案,通過簡單的配置即可實現(xiàn)強(qiáng)大的主題功能。未來版本將繼續(xù)優(yōu)化各平臺的兼容性和性能表現(xiàn)。

主題開發(fā)文檔

開發(fā)文檔
GitHub

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容