实现原理:
- 获取当前
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) returnconst 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').versionconst 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 = elNewStyledocument.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. 关闭 dialogclosed()}</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>
