背景
目前接手的是一个cordova的项目,最近一个迭代的到一个需求,需要为不同的租户定制不同的主题,我们希望租户的主题能跟随租户定制。
实现思路
- 首先通过接口或者容器拿到主题标识
- 通过标识在本地匹配对应的主题数据(就是各个部分的颜色)
- 通过ajax请求本地css文件(就是我们需要更改颜色的所有样式)
- 通过我们本地获取主题色把css文件进行替换(replace 原来的锚点)
最后动态生成style标签写入
代码部分
因为是cordova的项目,我们提前在容器就为客户进行定制,写入了租户id.我们通过本地定制的样式进行匹配颜色
var themeConfigJSON = { "1097660": { standard: '#40c9c9', dark: '#24aca8', active: '#24aca8', light: '#37dbdb', selected: '#40c9c9', activeColor: '#24aca8', customerColor: '#24aca8', arrangeColor: '#40c9c9', arrangeColor1: '#efffff', } } //从容器中拿id navigator.appplugin.getAppInfo(function(id) { _this.updateStyle(id); });
这里就是你本地css文件,留下一些你比较好进行替换的锚点(这里@{text2},@{color1}都是自己创建的),同时需要注意的你的class权重,避免样式污染。如果你是vue项目你需要考虑取消掉模块的scoped,这里也要考虑全局样式的污染。
.theme_body{ top: @{text2}px; background: @{color1}; } .theme_tip{ padding-top: @{text1}px; }
这里就是替换逻辑,把提前的锚点全部进行替换,写入我们的主题色
changeStyle: function(style){ //text1 状态栏的高度 //text2 titleBar + 状态栏高度 var color = '#fff'; var height = 0; style = style.replace( /@{text1}/g, height); style = style.replace( /@{color1}/g, color); return style; },
获取到本地匹配的颜色后,我们读取本地css文件。将替换之后的css文件写入到Head中
loadStyle(){ var _this = this; var xmlhttp = new XMLHttpRequest(); xmlhttp.open('get', this.url, true); xmlhttp.setRequestHeader("Content-Type", "application/json"); xmlhttp.send(); xmlhttp.onreadystatechange = function (){ if(xmlhttp.readyState == 4){ //这里就是替换之后的 css数据 var styleContent = _this.changeStyle(xmlhttp.responseText); var style = document.createElement('style'); //这里就是本地路径的css url style.setAttribute('path',_this.url); style.innerHTML = styleContent.trim(); //最后写入到head中 document.head.appendChild(style); } }; },
总结
以上就是这个主题替换的核心思路,这个方法的好处就是,对于大量的主题有很好的拓展性。只需要增加对应的配置即可。缺点也很明显,在vue项目中需要有良好的命名习惯,尽量少用scoped,避免全局主题样式覆盖不了。同事class类名的权重也十分关键,现在less,sass成了比较常见的工具,class类名的嵌套已经成了习惯,全局覆盖变得更加痛苦。
改进方向
- 希望让全局覆盖变得不那么困难
- 初次加载的时候主题loading会有闪动