&nbs p;
当接到这个需求的时候, 百度 到业界关于主题切换的 方案 还挺多的,css链接替换、 classname 更改、less.modify VAR s、css in js 等等 ,但每一种方案听起来都是又累又贵。有没有那种代码侵入低, 小白 无脑又好维护的方案呢?那自然是有的,确切的说是css它本身就支持。
Css3 Variable
定义一个全局颜色变量, 改变 这个变量的值页面内所有引用这个变量的元素都会进行改变。好 简单 是不是?
// base.less :root { -- Primary : green; --warning: yellow; - -i nfo: wh IT e; --danger: red ; } // var.less @ PR imary: 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。
css vars ponyfill
是的,还真有polyfill能兼容IE: css-vars-ponyfill 。它 搞定 IE的方式大概是这样子的
+-------------------------+
| 获取页面内style标签内容 |
| 请求 外链 css内容 |
+-------------------------+
|
|
v
+-------------------------+ 是 +-------------------------+
| 内容 是否 含有var() | ----> | 标记为src |
+-------------------------+ +-------------------------+
| |
| 否 |
v v
+-------------------------+ +-------------------------+
| 标记为skip | | 将var(*)替换为 变量值 , |
| | | 新增style标签添加到head |
+-------------------------+ +-------------------------+
效果大概是这个样子的
简单粗暴又不失优雅,在支持css var的浏览器中不会进行处理,所以不需要担心性能问题( 是IE的问题,不是我的问题
)。 我们来改造一下代码
// Store /theme.js import cssVars From 'css-vars-ponyfill' export default { stat e: { 'primary': 'green', 'danger': 'white' }, mutations: { UPDATE_THEME(state, payload) { const variables = {} Object.assign(state, payload) Object.keys(state).for each ((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中看到这样的情况
因为 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变量。
// plu gin .js e xp ort 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& am p;ty PE =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}` } ) }
至此,我们可以愉快自如的切换主题了。
后记
通过如何[懒得写更多代码]来吸收新知识会更加有趣, 希望这篇 文章 能够帮助到你。
到此这篇关于基于Css Variable的主题切换完美解决方案(推荐)的文章就介绍到这了,更多相关css Variable的主题切换内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!
总结
以上是 为你收集整理的 基于Css Variable的主题切换完美解决方案(推荐) 全部内容,希望文章能够帮你解决 基于Css Variable的主题切换完美解决方案(推荐) 所遇到的问题。
如果觉得 网站内容还不错, 推荐好友。
查看更多关于基于Css Variable的主题切换完美解决方案(推荐)的详细内容...