html如何切换主题样式,基于Css Variable的主题切换完美解决方案(推荐)

efd97f99fdb3cb90fa1341edb251807c.png

当接到这个需求的时候,百度到业界关于主题切换的方案还挺多的,css链接替换、className更改、less.modifyVars、css in js等等,但每一种方案听起来都是又累又贵。有没有那种代码侵入低,小白无脑又好维护的方案呢?那自然是有的,确切的说是css它本身就支持。

Css3 Variable

定义一个全局颜色变量,改变这个变量的值页面内所有引用这个变量的元素都会进行改变。好简单是不是?

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

}

本文结束

个P,它不支持 IE 啊!!0202年还要兼容IE吗?是的,就是要兼容IE。

9ad5cb37bbb3877482106a73859d1956.png

css vars ponyfill

是的,还真有polyfill能兼容IE: css-vars-ponyfill 。它搞定IE的方式大概是这样子的

+-------------------------+

|   获取页面内style标签内容  |

|     请求外链css内容       |

+-------------------------+

|

|

v

+-------------------------+  是   +-------------------------+

|      内容是否含有var()    | ----> |        标记为src         |

+-------------------------+       +-------------------------+

|                                 |

| 否                              |

v                                 v

+-------------------------+       +-------------------------+

|       标记为skip         |       |   将var(*)替换为变量值,  |

|                         |       |  新增style标签添加到head  |

+-------------------------+       +-------------------------+

效果大概是这个样子的

b2310764855481da44ec181b60c397e0.png

简单粗暴又不失优雅,在支持css var的浏览器中不会进行处理,所以不需要担心性能问题( 是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

// 因为路由跳转后的页面会按需加载新的css资源,重新转换

const convertedPages = new Set()

router.afterEach((to) => {

if (convertedPages.has(to.path)) return

convertedPages.add(to.path)

context.store.dispatch('theme/changeTheme')

})

SSR项目闪屏问题优化

在SSR项目中用上述方案你可能会在IE中看到这样的情况

f0a559341e02624f11f193bcbf8578cb.gif

因为 css-vars-ponyfill

是依赖dom元素来实现转换的,在node中无法使用,所以从server直出未转换的css代码到client加载js文件转换css间存在一段样式空档。

+- - - - - - - - - - - - - - - - - - - -+

' 样式空窗期:                             '

'                                       '

+----------+     ' +----------------+     +------------+ '     +-------------+

| 发起请求  | --> ' |  SSR直出页面     | --> | 加载js依赖  | ' --> |  替换css变量 |

+----------+     ' +----------------+     +------------+ '     +-------------+

'                                       '

+- - - - - - - - - - - - - - - - - - - -+

解决这个问题也很简单,只需要在每个用到 css var 的地方加上一个兼容写法

@_primary: red

@primary: var(--primary)

:root{

--primary: @_primary

}

.theme {

color: @primary;

}

// 改为

.theme {

color: @_primary;

color: @primary;

}

在不支持css var的浏览器上会渲染默认颜色 red ,等待js加载完毕后ponyfill替换样式覆盖。

Webpack插件开发

手动在每个用到的地方添加兼容写法既幸苦又不好维护,这个时候我们需要了解一些 webpack 生命周期以及插件开发相关的知识,我们可以通过手写一个webpack插件,在 normalModuleLoader ( v5版本被废弃,使用NormalModule.getCompilationHooks(compilation).loader )的hooks中为所有css module添加一个loader来处理兼容代码。

笔者项目使用了less,注意webpack中loader执行顺序是 类似栈的先进后出 ,所以我需要把转换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项目同构会有2次compiler,如果module中存在loader则不继续添加

if (hasLoader(moduleContext.loaders, 'hackcss-loader.js')) {

return

}

let lessLoaderIndex = 0

// 项目用了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}`

}

)

}

至此,我们可以愉快自如的切换主题了。

f4154b7f85a49051a1a277f29201a558.gif

后记

通过如何“懒得写更多代码”来吸收新知识会更加有趣, 希望这篇文章能够帮助到你。

到此这篇关于基于Css Variable的主题切换完美解决方案(推荐)的文章就介绍到这了,更多相关css Variable的主题切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值