思路
获取document中所有的style标签,正则匹配替换里面含有主题色的字符串,并生成新的字符串append到body中,起到覆盖样式的作用
问题和解决
当样式字符串比较多时,正则匹配与替换这步操作耗时比较多,会阻塞当前页面,此时使用web-worker创建线程来解决
具体代码
1 安装js-cookie vue-worker
2 element-variables.scss
项目默认的自定义主题色, :export暴露主题色变量
/* 改变主题色变量 */
$--color-primary: #409EFF;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
:export {
color_primary:$--color-primary
}
3 change_theme.js
使用vue-worker创建web-worker线程, 匹配所有style标签的innerText, 然后循环匹配含有element主题色的样式块,例如....a{color:#409EFF;font-size:14px}.b{color:red}...
中只匹配.a{color:#409EFF;font-size:14px}
, 之后将匹配到的字符串进行replace操作,替换新的颜色主题,生成新的style插入或替换到body中,覆盖之前的代码,最后将新的主题色存于cookie
注意: web-worker中不能操作document和window对象,所以web-worker线程中只操作字符串
import Cookies from "js-cookie";
import variables from "@/assets/sass/element.variables.scss"; //element默认主题色
import VueWorker from "vue-worker"; //封装的web-worker插件
import Vue from "vue";
Vue.use(VueWorker);
const theme_color = variables.color_primary;
export default class Theme extends Vue {
constructor(color) {
super(color);
this.worker = "";
this.init_color = color; //类创建时接受的初始主题色
if (color && theme_color != color) { //若初始主题色与element主题色不同,执行换肤操作
this.init_color = theme_color;
this.set_stylestr(color);
}
}
create_color_worker() { //创建webworker线程, 生成新的style字符串
this.worker = this.$worker.create([
{
message: "filter_style_text",
func(stylearr, dcolor_regexp, newrggexp, newcolor) {
var newStyle = "";
stylearr.forEach((i, k) => { //样式字符串循环匹配含有主题色的样式字符串,并替换新的字符串
var match = "";
while ((match = dcolor_regexp.exec(i)) != null) {
newStyle += match[0].replace(newrggexp, newcolor);
}
});
return newStyle;
},
},
]);
}
set_stylestr(newcolor) { //换肤操作
this.$message({
message: "正在替换主题",
iconClass: "el-icon-loading",
duration: 0,
});
var stylearr = [];
Array.prototype.slice
.call(document.querySelectorAll("style"))
.forEach((i) => {
stylearr.push(i.innerText);
});
this.create_color_worker();
var dcolor_regexp = new RegExp( //初始颜色样式的正则
"[^}]+{[^}]+" + this.init_color + "[^}]*}",
"ig"
);
var newrggexp = new RegExp(this.init_color, "ig"); //替换颜色的正则
this.worker
.postMessage("filter_style_text", [
stylearr,
dcolor_regexp,
newrggexp,
newcolor,
])
.then((newStyle) => {
let styleTag = document.getElementById("fugai_theme");
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.setAttribute("id", "fugai_theme");
document.body.appendChild(styleTag);
}
styleTag.innerText = newStyle;
Cookies.set("theme_color", newcolor);
this.worker = null; //清除webworker实例
this.$message.closeAll();
this.$message({
message: "替换成功",
type: "success",
});
});
}
}
4 main.js 部分代码
项目初始时创建Theme对象,传入的颜色为之前换肤的颜色
import "@/assets/sass/element.variables.scss";
import Theme from "@/util/change_theme.js"
var themecolor = Cookies.get("theme_color")
Vue.prototype.$theme=new Theme( themecolor );
5 换肤操作
在需要的页面中调用this.$theme..set_stylestr("blue")
,此处为替换蓝色主题