实现原理:
- 获取当前
element-plus
的所有样式 - 找到我们想要替换的样式部分,通过正则完成替换
- 把替换后的样式写入到
style
标签中,利用样式优先级的特性,替代固有样式
实现步骤:
- 获取当前
element-plus
的所有样式 - 定义我们要替换之后的样式
- 在原样式中,利用正则替换新样式
- 把替换后的样式写入到
style
标签中方案落地
创建utils/theme
工具类,写入两个方法 ```javascript /**- 写入新样式到 style
- @param {*} elNewStyle element-plus 的新样式
- @param {} isNewStyleTag 是否生成新的 style 标签 / export const writeNewStyle = elNewStyle => {
}
/**
- 根据主色值,生成最新的样式表 */ export const generateNewStyle = primaryColor => {
}
那么接下来我们先实现第一个方法 `generateNewStyle`,在实现的过程中,我们需要安装两个工具类:
1. [rgb-hex](https://www.npmjs.com/package/rgb-hex):转换RGB(A)颜色为十六进制
1. [css-color-function](https://www.npmjs.com/package/css-color-function):在CSS中提出的颜色函数的解析器和转换器
然后还需要写入一个 **颜色转化计算器 **`**formula.json**`<br />创建 `constants/formula.json` (https://gist.github.com/benfrain/7545629)
```json
{
"shade-1": "color(primary shade(10%))",
"light-1": "color(primary tint(10%))",
"light-2": "color(primary tint(20%))",
"light-3": "color(primary tint(30%))",
"light-4": "color(primary tint(40%))",
"light-5": "color(primary tint(50%))",
"light-6": "color(primary tint(60%))",
"light-7": "color(primary tint(70%))",
"light-8": "color(primary tint(80%))",
"light-9": "color(primary tint(90%))",
"subMenuHover": "color(primary tint(70%))",
"subMenuBg": "color(primary tint(80%))",
"menuHover": "color(primary tint(90%))",
"menuBg": "color(primary)"
}
准备就绪后,我们来实现 generateNewStyle
方法:
import color from 'css-color-function'
import rgbHex from 'rgb-hex'
import formula from '@/constant/formula.json'
import axios from 'axios'
/**
* 根据主色值,生成最新的样式表
*/
export const generateNewStyle = async primaryColor => {
const colors = generateColors(primaryColor)
let cssText = await getOriginalStyle()
// 遍历生成的样式表,在 CSS 的原样式中进行全局替换
Object.keys(colors).forEach(key => {
cssText = cssText.replace(
new RegExp('(:|\\s+)' + key, 'g'),
'$1' + colors[key]
)
})
return cssText
}
/**
* 根据主色生成色值表
*/
export const generateColors = primary => {
if (!primary) return
const colors = {
primary
}
Object.keys(formula).forEach(key => {
const value = formula[key].replace(/primary/g, primary)
colors[key] = '#' + rgbHex(color.convert(value))
})
return colors
}
/**
* 获取当前 element-plus 的默认样式表
*/
const getOriginalStyle = async () => {
const version = require('element-plus/package.json').version
const url = `https://unpkg.com/element-plus@${version}/dist/index.css`
const { data } = await axios(url)
// 把获取到的数据筛选为原样式模板
return getStyleTemplate(data)
}
/**
* 返回 style 的 template
*/
const getStyleTemplate = data => {
// element-plus 默认色值
const colorMap = {
'#3a8ee6': 'shade-1',
'#409eff': 'primary',
'#53a8ff': 'light-1',
'#66b1ff': 'light-2',
'#79bbff': 'light-3',
'#8cc5ff': 'light-4',
'#a0cfff': 'light-5',
'#b3d8ff': 'light-6',
'#c6e2ff': 'light-7',
'#d9ecff': 'light-8',
'#ecf5ff': 'light-9'
}
// 根据默认色值为要替换的色值打上标记
Object.keys(colorMap).forEach(key => {
const value = colorMap[key]
data = data.replace(new RegExp(key, 'ig'), value)
})
return data
}
接下来处理 writeNewStyle
方法:
/**
* 写入新样式到 style
* @param {*} elNewStyle element-plus 的新样式
* @param {*} isNewStyleTag 是否生成新的 style 标签
*/
export const writeNewStyle = elNewStyle => {
const style = document.createElement('style')
style.innerText = elNewStyle
document.head.appendChild(style)
}
最后在 SelectColor.vue
中导入这两个方法:
...
<script setup>
...
import { generateNewStyle, writeNewStyle } from '@/utils/theme'
...
/**
* 确定
* 1. 修改主题色
* 2. 保存最新的主题色
* 3. 关闭 dialog
*/
const comfirm = async () => {
// 1.1 获取主题色
const newStyleText = await generateNewStyle(mColor.value)
// 1.2 写入最新主题色
writeNewStyle(newStyleText)
// 2. 保存最新的主题色
store.commit('theme/setMainColor', mColor.value)
// 3. 关闭 dialog
closed()
}
</script>
一些处理完成之后,我们可以在 profile
中通过一些代码进行测试:
<el-row>
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
</el-row>
element-plus 新主题的立即生效
目前 element-plus
的主题变更还有一个小问题,那就是:在刷新页面后,新主题会失效
出现这个问题的原因:因为没有写入新的 **style**
所以我们只需要在 应用加载后,写入 **style**
即可
那么写入的时机,我们可以放入到 app.vue
中
<script setup>
import { useStore } from 'vuex'
import { generateNewStyle, writeNewStyle } from '@/utils/theme'
const store = useStore()
generateNewStyle(store.getters.mainColor).then(newStyleText => {
writeNewStyle(newStyleText)
})
</script>