小編給大家分享一下基于Css Variable的主題切換示例,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在泰安等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站建設(shè),成都營銷網(wǎng)站建設(shè),外貿(mào)營銷網(wǎng)站建設(shè),泰安網(wǎng)站建設(shè)費(fèi)用合理。
當(dāng)接到這個(gè)需求的時(shí)候,百度到業(yè)界關(guān)于主題切換的方案還挺多的,css鏈接替換、className更改、less.modifyVars、css in js等等,但每一種方案聽起來都是又累又貴。有沒有那種代碼侵入低,小白無腦又好維護(hù)的方案呢?那自然是有的,確切的說是css它本身就支持。
Css3 Variable
定義一個(gè)全局顏色變量,改變這個(gè)變量的值頁面內(nèi)所有引用這個(gè)變量的元素都會(huì)進(jìn)行改變。好簡(jiǎn)單是不是?
// base.less :root { --primary: green; --warning: yellow; --info: white; --danger: red; } // var.less @primary: var(--primary) @danger: var(--danger) @info: var(--info) // page.less .header { background-color: @primary; color: @info; } .content { border: 1px solid @danger; }
// change.js function changeTheme(themeObj) { const vars = Object.keys(themeObj).map(key => `--${key}:${themeObj[key]}`).join(';') document.documentElement.setAttribute('style', vars) }
本文結(jié)束
個(gè)P,它不支持 IE?。?!0202年還要兼容IE嗎?是的,就是要兼容IE。
css vars ponyfill
是的,還真有polyfill能兼容IE: css-vars-ponyfill 。它搞定IE的方式大概是這樣子的
+-------------------------+
| 獲取頁面內(nèi)style標(biāo)簽內(nèi)容 |
| 請(qǐng)求外鏈css內(nèi)容 |
+-------------------------+
|
|
v
+-------------------------+ 是 +-------------------------+
| 內(nèi)容是否含有var() | ----> | 標(biāo)記為src |
+-------------------------+ +-------------------------+
| |
| 否 |
v v
+-------------------------+ +-------------------------+
| 標(biāo)記為skip | | 將var(*)替換為變量值, |
| | | 新增style標(biāo)簽添加到head |
+-------------------------+ +-------------------------+
效果大概是這個(gè)樣子的
簡(jiǎn)單粗暴又不失優(yōu)雅,在支持css var的瀏覽器中不會(huì)進(jìn)行處理,所以不需要擔(dān)心性能問題( 是IE的問題,不是我的問題
)。 我們來改造一下代碼
// store/theme.js import cssVars from 'css-vars-ponyfill' export default { state: { 'primary': 'green', 'danger': 'white' }, mutations: { UPDATE_THEME(state, payload) { const variables = {} Object.assign(state, payload) Object.keys(state).forEach((key) => { variables[`--${key}`] = state[key] }) cssVars({ variables }) } }, actions: { changeTheme({ commit }, theme = {}) { commit('UPDATE_THEME', theme) } } } // router.js // 因?yàn)槁酚商D(zhuǎn)后的頁面會(huì)按需加載新的css資源,重新轉(zhuǎn)換 const convertedPages = new Set() router.afterEach((to) => { if (convertedPages.has(to.path)) return convertedPages.add(to.path) context.store.dispatch('theme/changeTheme') })
SSR項(xiàng)目閃屏問題優(yōu)化
在SSR項(xiàng)目中用上述方案你可能會(huì)在IE中看到這樣的情況
因?yàn)?css-vars-ponyfill
是依賴dom元素來實(shí)現(xiàn)轉(zhuǎn)換的,在node中無法使用,所以從server直出未轉(zhuǎn)換的css代碼到client加載js文件轉(zhuǎn)換css間存在一段樣式空檔。
+- - - - - - - - - - - - - - - - - - - -+
' 樣式空窗期: '
' '
+----------+ ' +----------------+ +------------+ ' +-------------+
| 發(fā)起請(qǐng)求 | --> ' | SSR直出頁面 | --> | 加載js依賴 | ' --> | 替換css變量 |
+----------+ ' +----------------+ +------------+ ' +-------------+
' '
+- - - - - - - - - - - - - - - - - - - -+
解決這個(gè)問題也很簡(jiǎn)單,只需要在每個(gè)用到 css var
的地方加上一個(gè)兼容寫法
@_primary: red @primary: var(--primary) :root{ --primary: @_primary } .theme { color: @primary; } // 改為 .theme { color: @_primary; color: @primary; }
在不支持css var的瀏覽器上會(huì)渲染默認(rèn)顏色 red
,等待js加載完畢后ponyfill替換樣式覆蓋。
Webpack插件開發(fā)
手動(dòng)在每個(gè)用到的地方添加兼容寫法既幸苦又不好維護(hù),這個(gè)時(shí)候我們需要了解一些 webpack 生命周期以及插件開發(fā)相關(guān)的知識(shí),我們可以通過手寫一個(gè)webpack插件,在 normalModuleLoader
( v5版本被廢棄,使用NormalModule.getCompilationHooks(compilation).loader )的hooks中為所有css module添加一個(gè)loader來處理兼容代碼。
筆者項(xiàng)目使用了less,注意webpack中l(wèi)oader執(zhí)行順序是 類似棧的先進(jìn)后出,所以我需要把轉(zhuǎn)換loader添加到less-loader之前,確保我們處理的是編譯后的css var寫法而非less變量。
// plugin.js export default class HackCss { constructor (theme = {}) { this.themeVars = theme } apply(compiler) { compiler.hooks.thisCompilation.tap('HackCss', (compilation) => { compilation.hooks.normalModuleLoader.tap( 'HackCss', (_, moduleContext) => { if (/\.vue\?vue&type=style/.test(moduleContext.userRequest)) { // ssr項(xiàng)目同構(gòu)會(huì)有2次compiler,如果module中存在loader則不繼續(xù)添加 if (hasLoader(moduleContext.loaders, 'hackcss-loader.js')) { return } let lessLoaderIndex = 0 // 項(xiàng)目用了less,找到less-loader的位置 moduleContext.loaders.forEach((loader, index) => { if (/less-loader/.test(loader.loader)) { lessLoaderIndex = index } }) moduleContext.loaders.splice(lessLoaderIndex, 0, { loader: path.resolve(__dirname, 'hackcss-loader.js'), options: this.themeVars }) } } ) }) } }) } // loader.js const { getOptions } = require('loader-utils') module.exports = function(source) { if (/module\.exports/.test(source)) return source const theme = getOptions(this) || {} return source.replace( /\n(.+)?var\(--(.+)?\)(.+)?;/g, (content, before, name, after = '') => { const [key, indent] = before.split(':') const add = after.split(';')[0] return `\n${key}:${indent}${theme[name]}${after}${add};${content}` } ) }
至此,我們可以愉快自如的切換主題了。
以上是“基于Css Variable的主題切換示例”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!