<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          接到“網(wǎng)站動(dòng)態(tài)換主題”的需求,我是如何踩坑的

          共 11587字,需瀏覽 24分鐘

           ·

          2021-03-03 23:31


          需求背景


          隨著業(yè)務(wù)的發(fā)展,客戶的需求也會(huì)變得更加多樣化,產(chǎn)品后期就需要有自定義界面的能力,于是出現(xiàn)了“動(dòng)態(tài)換主題”的需求。

          設(shè)計(jì)部門的同事讓我們可以參考Ant Design色板生成算法演進(jìn)之路

          后面我們動(dòng)態(tài)計(jì)算色板也是采用了目前 Ant Design 的算法, @ant-design/colors

          但是切換主題的方式,經(jīng)驗(yàn)證并不能很完美的適用于我們微前端項(xiàng)目。



          設(shè)計(jì)標(biāo)準(zhǔn)


          以上色系變量表是我們本次最終需要的全部變量

          其中每種色系分為兩種,h開頭的和a開頭的,a開頭的通過調(diào)整透明度來生成,h 開頭的一組由 base 色通過ant-design 的動(dòng)態(tài)計(jì)算生成

          本色系設(shè)計(jì)由合思設(shè)計(jì)團(tuán)隊(duì) 出品,中性色為直接定義死的,不做計(jì)算;

          可配置的基礎(chǔ)色分為

          • 品牌色(brand-base):#22B2CC

          • 警告色(warning-base):#FAAD14

          • 危險(xiǎn)色(danger-base):#F5222D

          • 提示色(info-base):#1890FF

          • 成功色(success-base):#52C41A



          前端方案



          我在接到需求后,經(jīng)過和公司架構(gòu)師及其他同事的探討后,漸漸產(chǎn)出了以下幾種方案,一步步踩坑過來。



          方案一:

          兩種主題模式(light/dark),需要分別兩個(gè) less 文件來定義這兩套顏色變量


          Light-colors.less

          dark-colors.less

          兩種模式下,值固定不變的顏色變量單獨(dú)定義一個(gè)文件 common-colors.less ,然后我選擇將三個(gè)文件引入到同一個(gè)index 中輸出使用,需要使用的地方只需要引入index.less 即可。

          但是問題來了

          1、如何在index.less 中來判斷使用light-colors 還是 dark-colors 呢?

          @import 只能定義在文件頂部,也沒有任何可以做條件引入的方法

          2、如何根據(jù)品牌色動(dòng)態(tài)計(jì)算色系變量值呢?

          計(jì)算為色系變量值是通過js產(chǎn)出一個(gè)數(shù)組,想要導(dǎo)入到一個(gè)less文件中,再引入使用,想要?jiǎng)討B(tài)切換的話,需要用到 less的modifyVars方法, 也是Ant Design 官方提供的方式,接著我們嘗試



          方案二:

          less  的modifyVars方法是是基于 less 在瀏覽器中的編譯來實(shí)現(xiàn)。所以在引入less文件的時(shí)候需要通過link方式引入,然后基于less.js中的方法來進(jìn)行修改變量




          less.modifyVars({
           '@themeColor': '#22B2CC'
          });
          <link rel="stylesheet/less" type="text/css" href="./src/less/theme-colors.less" />
          // color 傳入顏色值
          changeTheme (color) {
             less.modifyVars({  // 調(diào)用 `less.modifyVars` 方法來改變變量值'
                  @themeColor':color
                  })
            .then(() => {
                  console.log('修改成功');
            });
          };
          • 需要引入less編譯器,太大了,嚴(yán)重影響性能;

          • 需要webpack 配置,無法多個(gè)進(jìn)程間共享變量,不適用于微前端項(xiàng)目。

          • 這種方法僅限于用less的項(xiàng)目才能使用,如果你項(xiàng)目使用的是sass,是沒有類似 less.modifyVars 這種解決方案的。




          方案三:

          1、在webpack構(gòu)建時(shí),通過 webpack-theme-color-replacer這個(gè)插件從所有輸出的css文件中提取主題顏色樣式,并創(chuàng)建一個(gè)僅包含顏色樣式的'theme-colors.css'文件。在網(wǎng)頁的運(yùn)行時(shí),客戶端部分下載此css文件,然后將顏色動(dòng)態(tài)替換為新的自定義顏色,能夠滿足更靈活豐富的功能場景,性能出色。

          2、@ant-design/colors 來動(dòng)態(tài)計(jì)算出品牌色系和功能色系。

          3、可以動(dòng)態(tài)的切換品牌色來獲取整個(gè)主題的切換。




          色系通過 提供的基準(zhǔn)色, 自動(dòng)計(jì)算及輸出的顏色集合:

          通過計(jì)算就可以輸出整個(gè)色系數(shù)組如下:

          需要設(shè)置顏色的地方就可以直接使用定義的這些變量,需要切換主題或者顏色的時(shí)候,傳入主題模式、品牌色重新計(jì)算,就可以實(shí)現(xiàn)動(dòng)態(tài)切換主題了。

          看似沒啥問題,但是在我們的系統(tǒng)里,問題來了。

          因?yàn)槲覀兪俏⑶岸隧?xiàng)目,拆包出大概二三十個(gè)項(xiàng)目,創(chuàng)建一個(gè)僅包含顏色樣式的theme-colors.css文件這一步是運(yùn)行在編譯時(shí)的,那么每個(gè)子項(xiàng)目如果沒有配置這個(gè)webpack,就無法共享該變量,在開發(fā)編譯階段就會(huì)報(bào)錯(cuò)!即使每個(gè)項(xiàng)目都配置了這樣的webpack構(gòu)建,也會(huì)創(chuàng)建各自的 theme-colors.css 文件,更改主題時(shí)候也無法同步切換,一樣的坑爹?。?!

          由此可見,即使一個(gè)方案很好很成熟,也不是滿足所有項(xiàng)目的。落實(shí)一個(gè)方案的時(shí)候,要根據(jù)自己的項(xiàng)目情況做分析,做出一個(gè)符合自身項(xiàng)目的解決方案才是硬道理,而不是一味的生搬硬套。

          于是該方案斃掉,繼續(xù)思考下一個(gè)方案。



          方案四:

          時(shí)代好了,瀏覽器普遍支持Css3變量了,基于Css3 Variable 共享全局主題變量看起來就是一個(gè)很通用的方案了。



          首先定義一個(gè)全局變量,改變這個(gè)變量的值,頁面中所有引用這個(gè)變量的元素都會(huì)進(jìn)行改變,既沒有 less 的編譯過程,也不存在什么性能問題,這不就是我們最期望的動(dòng)態(tài)換膚方案嗎?

          Css3 Variable的用法就是給變量加--前綴,涉及到主題色的都改成var(--themeColor)這種方式

          我們先查一下兼容性

          主流瀏覽器基本全部兼容,對于大多數(shù)互聯(lián)網(wǎng)企業(yè)產(chǎn)品完全夠用了,但是對于某些還在使用IE 瀏覽器的產(chǎn)品就需要ponyfill 方案兼容了。


          也確實(shí)有這樣一個(gè) polyfill 能兼容IE: css-vars-ponyfill

          這個(gè)polyfill 只會(huì)在不支持Css3 Variable 的環(huán)境會(huì)生效

          我們開始寫代碼了:

          1、建一個(gè)存放公共css變量的js文件(variable.js),將需要定義的css變量存放到該js文件,品牌色及功能色等通過antd算法計(jì)算獲得;

          import { getAlphaColor } from "./themeUtils";
          const { generate } = require("@ant-design/colors");
          import baseTheme from "./baseTheme";
          import lightTheme from "./lightTheme";
          import darkTheme from "./darkTheme";
          import { functionalColorsBase, grayBase } from "./colors";

          const themeModes = {
           light: undefined,
           dark: {
             theme: "dark",
             backgroundColor: grayBase,
          },
          };

          // 獲取品牌色系
          export const getBrandColors = (color, mode) => {
           let options = themeModes[mode];
           return generate(color, options);
          };

          // 獲取功能色系
          export const getFunctionalColors = (mode) => {
           let options = themeModes[mode];
           let { success, warning, danger, info } = functionalColorsBase;
           const successColors = generate(success, options);
           const warningColors = generate(warning, options);
           const dangerColors = generate(danger, options);
           const infoColors = generate(info, options);
           return {
             success: successColors,
             warning: warningColors,
             danger: dangerColors,
             info: infoColors,
          };
          };

          // 輸出色板
          export const modifyVars = (color, mode) => {
           const brandColors = getBrandColors(color, mode);
           const { success, warning, danger, info } = getFunctionalColors(mode);
           const colors = {
             ...baseTheme,
             "--brand-base": brandColors[5],
             "--success-base": success[5],
             "--warning-base": warning[5],
             "--danger-base": danger[5],
             "--info-base": info[5],
             "--h-brand-1": brandColors[0],
             "--h-brand-2": brandColors[1],
             "--h-brand-3": brandColors[2],
             "--h-brand-4": brandColors[3],
             "--h-brand-5": brandColors[4],
             "--h-brand-6": brandColors[5],
             "--h-brand-7": brandColors[6],
             "--h-brand-8": brandColors[7],
             "--h-brand-9": brandColors[8],
             "--h-brand-10": brandColors[9],
             "--h-success-1": success[0],
             "--h-success-2": success[1],
             "--h-success-3": success[2],
             "--h-success-4": success[3],
             "--h-success-5": success[4],
             "--h-success-6": success[5],
             "--h-success-7": success[6],
             "--h-success-8": success[7],
             "--h-success-9": success[8],
             "--h-success-10": success[9],
             "--h-warning-1": warning[0],
             "--h-warning-2": warning[1],
             "--h-warning-3": warning[2],
             "--h-warning-4": warning[3],
             "--h-warning-5": warning[4],
             "--h-warning-6": warning[5],
             "--h-warning-7": warning[6],
             "--h-warning-8": warning[7],
             "--h-warning-9": warning[8],
             "--h-warning-10": warning[9],
             "--h-danger-1": danger[0],
             "--h-danger-2": danger[1],
             "--h-danger-3": danger[2],
             "--h-danger-4": danger[3],
             "--h-danger-5": danger[4],
             "--h-danger-6": danger[5],
             "--h-danger-7": danger[6],
             "--h-danger-8": danger[7],
             "--h-danger-9": danger[8],
             "--h-danger-10": danger[9],
             "--h-info-1": info[0],
             "--h-info-2": info[1],
             "--h-info-3": info[2],
             "--h-info-4": info[3],
             "--h-info-5": info[4],
             "--h-info-6": info[5],
             "--h-info-7": info[6],
             "--h-info-8": info[7],
             "--h-info-9": info[8],
             "--h-info-10": info[9],
          };
           const darkConfigableTheme = {
             "--a-brand-1": getAlphaColor(brandColors[5], 0.04),
             "--a-brand-2": getAlphaColor(brandColors[5], 0.08),
             "--a-brand-3": getAlphaColor(brandColors[5], 0.16),
             "--a-brand-4": getAlphaColor(brandColors[5], 0.24),
             "--a-brand-5": getAlphaColor(brandColors[5], 0.32),
             "--a-brand-6": getAlphaColor(brandColors[5], 0.4),
             "--a-brand-7": getAlphaColor(brandColors[5], 0.52),
             "--a-brand-8": getAlphaColor(brandColors[5], 0.64),
             "--a-brand-9": getAlphaColor(brandColors[5], 0.76),
             "--a-brand-10": getAlphaColor(brandColors[5], 0.88),

             "--a-success-1": getAlphaColor(success[5], 0.04),
             "--a-success-2": getAlphaColor(success[5], 0.08),
             "--a-success-3": getAlphaColor(success[5], 0.16),
             "--a-success-4": getAlphaColor(success[5], 0.24),
             "--a-success-5": getAlphaColor(success[5], 0.32),
             "--a-success-6": getAlphaColor(success[5], 0.4),
             "--a-success-7": getAlphaColor(success[5], 0.52),
             "--a-success-8": getAlphaColor(success[5], 0.64),
             "--a-success-9": getAlphaColor(success[5], 0.76),
             "--a-success-10": getAlphaColor(success[5], 0.88),

             "--a-warning-1": getAlphaColor(warning[5], 0.04),
             "--a-warning-2": getAlphaColor(warning[5], 0.08),
             "--a-warning-3": getAlphaColor(warning[5], 0.16),
             "--a-warning-4": getAlphaColor(warning[5], 0.24),
             "--a-warning-5": getAlphaColor(warning[5], 0.32),
             "--a-warning-6": getAlphaColor(warning[5], 0.4),
             "--a-warning-7": getAlphaColor(warning[5], 0.52),
             "--a-warning-8": getAlphaColor(warning[5], 0.64),
             "--a-warning-9": getAlphaColor(warning[5], 0.76),
             "--a-warning-10": getAlphaColor(warning[5], 0.88),

             "--a-danger-1": getAlphaColor(danger[5], 0.04),
             "--a-danger-2": getAlphaColor(danger[5], 0.08),
             "--a-danger-3": getAlphaColor(danger[5], 0.16),
             "--a-danger-4": getAlphaColor(danger[5], 0.24),
             "--a-danger-5": getAlphaColor(danger[5], 0.32),
             "--a-danger-6": getAlphaColor(danger[5], 0.4),
             "--a-danger-7": getAlphaColor(danger[5], 0.52),
             "--a-danger-8": getAlphaColor(danger[5], 0.64),
             "--a-danger-9": getAlphaColor(danger[5], 0.76),
             "--a-danger-10": getAlphaColor(danger[5], 0.88),

             "--a-info-1": getAlphaColor(info[5], 0.04),
             "--a-info-2": getAlphaColor(info[5], 0.08),
             "--a-info-3": getAlphaColor(info[5], 0.16),
             "--a-info-4": getAlphaColor(info[5], 0.24),
             "--a-info-5": getAlphaColor(info[5], 0.32),
             "--a-info-6": getAlphaColor(info[5], 0.4),
             "--a-info-7": getAlphaColor(info[5], 0.52),
             "--a-info-8": getAlphaColor(info[5], 0.64),
             "--a-info-9": getAlphaColor(info[5], 0.76),
             "--a-info-10": getAlphaColor(info[5], 0.88),
          };
           const lightModeColors = { ...lightTheme, ...colors };
           const darkModeColors = { ...darkTheme, ...darkConfigableTheme, ...colors };
           console.log(lightModeColors, "=====", darkModeColors);
           return mode == "light" ? lightModeColors : darkModeColors;
          };

          2、頁面使用css變量,無論是web主項(xiàng)目,還是各個(gè)plugin子項(xiàng)目都可以共享變量,不需要引入任何依賴,設(shè)計(jì)圖標(biāo)注與代碼對應(yīng)關(guān)系:

          UI
          CODE
          h-brand-1
          var(--h-brand-1)


          3、封裝切換主題的js,在項(xiàng)目入口做初始化調(diào)用,支持更改light和dark模式,及變更品牌色基準(zhǔn)色

          import { brandBase, modifyVars } from "./variable";
          import cssVars from "css-vars-ponyfill";

          const key = "data-theme";

          // 獲取當(dāng)前主題
          export const getTheme = (mode, color) => {
           const localTheme = localStorage.getItem(key);
           const dataTheme = localTheme
             ? JSON.parse(localTheme)
            : {
                 color: color || brandBase,
                 mode: mode || "light",
              };
           return dataTheme;
          };

          // 初始化主題
          export const initTheme = (mode, color) => {
           const dataTheme = getTheme(mode, color);
           document.documentElement.setAttribute("data-theme", dataTheme.mode);
           cssVars({
             watch: true,
             // 當(dāng)添加,刪除或修改其<link>或<style>元素的禁用或href屬性時(shí),ponyfill將自行調(diào)用
             variables: modifyVars(dataTheme.color, dataTheme.mode), // variables 自定義屬性名/值對的集合
             onlyLegacy: false, // false  默認(rèn)將css變量編譯為瀏覽器識(shí)別的css樣式  true 當(dāng)瀏覽器不支持css變量的時(shí)候?qū)ss變量編譯為識(shí)別的css
          });
          };

          // 變更主題
          export const changeTheme = (mode, color) => {
           const dataTheme = {
             color: color || brandBase,
             mode: mode || "light",
          };
           localStorage.setItem(key, JSON.stringify(dataTheme));
           document.documentElement.setAttribute("data-theme", dataTheme.mode);
           cssVars({
             watch: true,
             variables: modifyVars(dataTheme.color, dataTheme.mode),
             onlyLegacy: false,
          });
          };

          4、在切換主題的按鈕組件中調(diào)用 changeTheme切換主題


          最終效果,目前只有部分掃雷了部分頁面,控制開關(guān)為臨時(shí)征用側(cè)邊欄:




          總結(jié)



          至此,一個(gè)微前端項(xiàng)目的動(dòng)態(tài)換膚方案已經(jīng)實(shí)現(xiàn),大家如果有更好的方案,歡迎補(bǔ)充哦~

          注:該方案出自合思大前端團(tuán)隊(duì) ,北京和南昌均有技術(shù)團(tuán)隊(duì),如果你有考慮新的工作機(jī)會(huì),歡迎投簡歷!

          ??愛心三連擊

          1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的點(diǎn)贊,在看是我創(chuàng)作的動(dòng)力。

          2.關(guān)注公眾號程序員成長指北,回復(fù)「1」加入高級前端交流群!「在這里有好多 前端 開發(fā)者,會(huì)討論 前端 Node 知識(shí),互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 130
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲最大免费在线观看视频 | 欧美成人精品H | 伊人电影AV | 午夜视频在线播放 | 一级黄色视频在吗哪看 |