<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>

          前端 “一鍵換膚“ 的幾種方案

          共 10541字,需瀏覽 22分鐘

           ·

          2022-03-10 23:42

          大廠技術  高級前端  Node進階

          點擊上方 程序員成長指北,關注公眾號

          回復1,加入高級Node交流群


          前端 “一鍵換膚“ 的幾種方案

          前言

          現(xiàn)在越來越多的網站都提供了擁有換膚(切換主題)功能,如 ElementUI,既是為了迎合用戶需求,或是為了凸顯自己特點,因此提供了個性化定制的功能.

          其實之前就想了解和實現(xiàn) “一鍵換膚” 功能,但是由于種種原因一直拖到了現(xiàn)在.

          skin.gif

          CSS 樣式覆蓋實現(xiàn)

          核心

          通過切換 css 選擇器的方式實現(xiàn)主題樣式的切換.

          • 在組件中保留不變的樣式,將需要變化的樣式進行抽離
          • 提供多種樣式,給不同的主題定義一個對應的 CSS 選擇器
          • 根據(jù)不同主題設置不同的樣式

          實現(xiàn)

          下面通過 vuex 存儲和控制全局的主題色,其代碼如下:

          import { createStore } from 'vuex'

          // 創(chuàng)建一個新的 store 實例
          const store = createStore({
            state () {
              return {
                theme'light'
              }
            },
            mutations: {
              setTheme (state, payload) {
                state.theme = payload
                document.querySelector('body').className = payload
              }
            }
          })

          export default store
          復制代碼

          在 template 模板中通過 vuex 中的主題設置對應類名,如頭部代碼如下:

          <template>
            <div :class="['header', store.state.theme]">
              <span>{{title}}</span>
              <input v-model="checked" type="checkbox" class="switch" @change="changeTheme" />
            </div>
          </template>
          復制代碼

          下面 theme.css 中通過 .light 和 .dark 兩個類選擇器來區(qū)分明亮主題和暗黑主題,并且事先準備了它們對應的樣式,如下:

          /* light 默認主題*/
          body.light {
            background-color#fff;
          }

          .header.light {
            background-color#fff;
            border-bottom1px solid #d6d6d6;
            colorrgb(515050);
          }

          .list.light .title {
            colorrgb(515050);
          }
          .list.light .describe{
            colorrgb(158158158);
          }

          .list.light .left{
            border1px solid rgb(515050);
          }

          /* dark 暗黑主題 */
          body.dark {
            background-colorrgb(515050);
          }

          .header.dark {
            background-colorrgb(515050);
            border-bottom1px solid #fff;
            color#fff;
          }

          .list.dark .title {
            color#fff;
          }
          .list.dark .describe{
            colorrgb(201201201);
          }
          .list.dark .left{
            border1px solid #fff;
            background-color#fff;
          }
          復制代碼

          缺點

          • 多種主題樣式都要引入,導致代碼量增大
          • 樣式不易管理
          • 查找樣式復雜
          • 開發(fā)效率低
          • 拓展性差
          • ...

          實現(xiàn)多套 CSS 主題樣式

          核心

          實現(xiàn)多套 CSS 主題樣式,根據(jù)用戶切換操作,通過 link 標簽動態(tài)加載不同的主題樣式,主要解決了多個主題色被編譯到一個文件中導致單個文件過大.

          實現(xiàn)

          css 部分直接拆分成 ligth.css 和 dark.css 兩個文件:

          image.png

          設置主題部分的 setTheme.js 代碼如下:

          export default function setTheme(theme = 'ligth'{
            let link = document.querySelector('#theme-link')
            let href = "/theme/" + theme + ".css"
            
            if (!link) {
              let head = document.querySelector('head')
              link = document.createElement('link')
              link.id = '#theme-link'
              link.rel = "stylesheet"
              link.href = href
              head.appendChild(link)
            } else {
              link.href = href
            }
          }
          復制代碼

          缺點

          • 需要重復 CV 多份樣式文件進行單獨修改
          • 沒有單獨提取出可變的樣式部分
          • 需要提前知道打包后的文件路徑,否則可能導致主題樣式引入錯誤
          • ...

          CSS 變量實現(xiàn)

          核心

          通過 body.style.setProperty(key, value) 動態(tài)修改 body 上的 CSS 變量,使得頁面上的其他部分可以應用最新的 CSS 變量對應的樣式.

          img

          實現(xiàn)

          theme.css 中負責定義全局的 CSS 變量,代碼如下:

          /* 實現(xiàn)方式一 */
          :root {
            --theme-bg: initial; // 背景色
            --theme-color: initial; // 字體色
            --theme-boder-color: initial; // 邊框色
          }

          ====================================================

          /* 實現(xiàn)方式二 */
          /* 默認值:light */
          :root {
            --theme-bg#fff;
            --theme-colorrgb(515050);
            --theme-img-bg#fff;
            --theme-boder-color#d6d6d6;
          }

          /* 暗黑:dark */
          [data-theme='dark'] {
            --theme-bgrgb(515050);
            --theme-color#fff;
            --theme-boder-color#fff;
          }
          復制代碼

          themeUtil.js 中負責獲取當前對應樣式值,以及設置 body 上的 CSS 變量值,如下:

          const darkTheme = 'rgb(51, 50, 50)'
          const lightTheme = '#fff'
          const lightBorderTheme = '#d6d6d6'

          // 獲取對應的主題色值
          export const getThemeMap = (isLight) => {
            return {
              'theme-bg': isLight ? lightTheme : darkTheme,
              'theme-color': isLight ? darkTheme : lightTheme,
              'theme-boder-color': isLight ? lightBorderTheme : lightTheme,
            }
          }

          // 設置主題色值
          export const setTheme = (isLight = true) => {
            const themeMap = getThemeMap(isLight)
            const body = document.body
            /* 實現(xiàn)方式一 */
            Object.keys(themeMap).forEach(key => {
              body.style.setProperty(`--${key}`, themeMap[key])
            })
            
            /* 實現(xiàn)方式二 */
            // body.style.setProperty('data-theme', isLight ? 'light' : 'dark')
          }

          復制代碼

          通過 var() 在組件中應用對應 CSS 變量,比如在頭部中的使用:

          <style scoped>
          .header {
            ...省略
            color: var(--theme-color);
            border-bottom: 1px solid var(--theme-boder-color);
            background-color: var(--theme-bg);
          }
          ...省略
          </style>
          復制代碼

          缺點

          缺點就是兼容性不好

          img

          兼容

          通過 css-vars-ponyfill 對 CSS 變量進行兼容處理,themeUtil.js 中代碼改變如下:

          import cssVars from "css-vars-ponyfill";

          const darkTheme = 'rgb(51, 50, 50)'
          const lightTheme = '#fff'
          const lightBorderTheme = '#d6d6d6'

          // 這里定義的 鍵/值 對,是為了給 cssVars 傳參
          export const getThemeMap = (isLight) => {
            return {
              '--theme-bg': isLight ? lightTheme : darkTheme,
              '--theme-img-bg': lightTheme,
              '--theme-color': isLight ? darkTheme : lightTheme,
              '--theme-boder-color': isLight ? lightBorderTheme : lightTheme,
            }
          }

          export const setTheme = (isLight = true) => {
            const themeMap = getThemeMap(isLight)
            const body = document.body
            
            /* 實現(xiàn)方式一 */
            Object.keys(themeMap).forEach(key => {
              body.style.setProperty(key, themeMap[key])
            })
            
            /* 實現(xiàn)方式二 */
            // body.style.setProperty('data-theme', isLight ? 'light' : 'dark')
            
            // 實現(xiàn)兼容方案
            cssVars({
              watchtrue// 添加、刪除、修改 <link> 或 <style> 元素的禁用或 href 屬性時,ponyfill 將自行調用    
              variables: themeMap, // variables 自定義屬性名/值對的集合
              onlyLegacyfalse// false  默認將 css 變量編譯為瀏覽器識別的 css 樣式 ;true 當瀏覽器不支持css變量的時候將css變量編譯為識別的css  
            });
          }
          復制代碼

          主題圖片切換



          實現(xiàn)了前面的內容之后,現(xiàn)在給分別給 light 和 dark 主題添加一個 logo,這一部分其實很簡單了,下面的示例代碼是基于 Vue3 進行實現(xiàn)的

          // Header.vue
          <script setup>
          import { ref } from 'vue'
          import { setTheme } from '../style/themeUtil'

          defineProps({
            titleString
          })

          const checked = ref(false)

          const logoUrl = ref('')

          const loadImg = async () => {
            let name = !checked.value ? 'light' : 'dark'
            let ext = !checked.value ? 'png' : 'jpg'
            let res = await import(`../assets/logo-${name}.${ext}`)
            logoUrl.value = res.default
          }

          loadImg()

          const changeTheme = (event) => {
            setTheme(!checked.value)
            loadImg()
          }

          </script>

          <template>
            <div class="header">
              <img class="logo" :src="logoUrl" /
          >
              <span>{{ title }}</span>
              <input v-model="checked" type="checkbox" class="switch" @change="changeTheme" />
            </div>
          </
          template>
          復制代碼

          效果如下

          skin.gif

          最后

          以上就是目前了解到一些的換膚方案,以上全部基于 css 去實現(xiàn)的,不過知道了原理就可以結合 less 和 sass 進行更好的實現(xiàn)。如果有更好的方案,歡迎貼在評論區(qū)進行分享?。?!


          作者:熊的貓

          原文:https://juejin.cn/post/7063010855167721486

              
          Node 社群



          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。



          如果你覺得這篇內容對你有幫助,我想請你幫我2個小忙:

          1. 點個「在看」,讓更多人也能看到這篇文章
          2. 訂閱官方博客 www.inode.club 讓我們一起成長

          點贊和在看就是最大的支持??


          瀏覽 61
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  激情五月天网站 | 日韩欧美黄色电影网站 | 传媒-熊猫成人网 | 99精品免费| 欧美日韩国产在线手机 |