最近发现了一个非常好用的webpack插件: webpack-theme-color-replacer:
https://github.com/hzsrc/webpack-theme-color-replacer
顾名思义,可以轻松实现整个项目主题颜色的替换。
引用下插件作者的思路:
基本思路就是,webpack构建时,在emit事件(准备写入dist结果文件时)中,将即将生成的所有css文件的内容中 带有指定颜色的css规则单独提取出来,再合并为一个theme-colors.css输出文件。然后在切换主题色时,下载这个文件,并替换为需要的颜色,应用到页面上。这样,下载的样式中就只包含颜色相关的css规则,文件较小;同时它已经包含了项目中所有的css中的指定颜色样式,一次下载全部颜色样式都搞定。
首先安装插件:
npm install ---save webpack-theme-color-replacer
然后引入插件,修改配置:
https://github.com/hzsrc/ant-design-pro-vue/commit/8f7600143be405cf8de6c950070ac7a80abb1235#diff-0e893b3578ae3d5c8b98bac1c80756a7
运行时修改主题颜色:
https://github.com/hzsrc/ant-design-pro-vue/commit/8f7600143be405cf8de6c950070ac7a80abb1235#diff-807ddb6f50da674fe8e2a75236722637
具体代码示例如下:
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default
const getAntdSerials = (color) => {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#3BA1FE'), // 主色系列
// 改变样式选择器,解决样式覆盖问题
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)
module.exports = createThemeColorReplacerPlugin
关键是这个文件
然后 调用插件内的方法:
changeColor (newColor) {
var options = {
newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
changeUrl (cssUrl) {
console.log('cssUrl', cssUrl)
return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path
}
}
return client.changer.changeColor(options, Promise)
}
通过生成的这个css/theme-colors--.css文件,找到所有指color的css规则,然后进行统一替换,其中还包括你自己另外写的color。
changeColor: function (options, promiseForIE) {
var win = window // || global
var Promise = promiseForIE || win.Promise
var _this = this;
if (!theme_COLOR_config) {
theme_COLOR_config = win.__theme_COLOR_cfg
var later = retry()
if (later) return later
}
var oldColors = options.oldColors || theme_COLOR_config.colors || []
var newColors = options.newColors || []
var cssUrl = theme_COLOR_config.url || options.cssUrl;
if (options.changeUrl) {
cssUrl = options.changeUrl(cssUrl)
}
return new Promise(function (resolve, reject) {
if (isSameArr(oldColors, newColors)) {
resolve()
} else {
getCssText(cssUrl, setCssTo, resolve, reject)
}
})
function retry() {
if (!theme_COLOR_config) {
if (_this._tryNum < 9) {
_this._tryNum = _this._tryNum + 1
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(_this.changeColor(options, promiseForIE))
}, 100)
})
} else {
theme_COLOR_config = {}
}
}
}
function getCssText(url, setCssTo, resolve, reject) {
var elStyle = idMap[url] && document.getElementById(idMap[url]);
if (elStyle) {
oldColors = elStyle.color.split('|')
setCssTo(elStyle, elStyle.innerText)
resolve()
} else {
elStyle = document.querySelector(options.appendToEl || 'body')
.appendChild(document.createElement('style'))
idMap[url] = 'css_' + (+new Date())
elStyle.setAttribute('id', idMap[url])
_this.getCSSString(url, function (cssText) {
setCssTo(elStyle, cssText)
resolve()
}, reject)
}
}
function setCssTo(elStyle, cssText) {
cssText = _this.replaceCssText(cssText, oldColors, newColors)
elStyle.color = newColors.join('|')
elStyle.innerText = cssText
theme_COLOR_config.colors = newColors
}
},
然后到这,我有些迷惑:其实最终css文件的路径是从全局的window下面拿到的
theme_COLOR_config = win.__theme_COLOR_cfg
但是我不太明白的是__theme_COLOR_cfg这个属性没有从window下面找到,也不知道这个属性的值是何时填进去的,麻烦知道的大佬们告知一下,谢过。
引用资料链接:
https://segmentfault.com/a/1190000016061608
https://github.com/hzsrc/webpack-theme-color-replacer
https://github.com/hzsrc/ant-design-pro-vue/commit/8f7600143be405cf8de6c950070ac7a80abb1235#diff-0e893b3578ae3d5c8b98bac1c80756a7