一般而言一个组件库都会设计一套相对来说符合大众审美或产品需求的主题,但是主题定制需求永远都存在,所以组件库一般都会允许使用者自定义主题,我司的vue组件库hui的定制主题简单来说是通过修改预定义的scss变量的值来做到的,新体系下还做到了动态换肤,因为皮肤本质上是一种静态资源(CSS文件和字体文件),所以只需要约定一种方式来每次动态请求加载不同的文件就可以了,为了方便这一需求,还配套开发了一个Vessel脚手架的插件,只需要以配置文件的方式列出你需要修改的变量和值,一个命令就可以帮你生成对应的皮肤。
但是目前的换肤还存在几个问题, 一是不直观,无法方便实时的看到修改后的组件效果,二是建议修改的变量比较少,这很大原因也是因为问题一,因为不直观所以盲目修改后的效果可能达不到预期。
针对这几个问题,所以实现一个在线主题编辑器是一个有意义的事情,目前最流行的组件库之一的Element就支持主题在线编辑,地址:… ,本项目是在参考了Element的设计思想和界面效果后开发完成的,本文将开发思路分享出来,如果有一些不合理地方或有一些更好的实现方式,欢迎指出来一起讨论。
实现思路
主题在线编辑的核心其实就是以一种可视化的方式来修改主题对应scss变量的值。
项目总体分为前端和后端两个部分,前端主要负责管理主题列表、编辑主题和预览主题,后端主要负责返回变量列表和编译主题。
后端返回主题可修改的变量信息,前端生成对应的控件,用户可进行修改,修改后立即将修改的变量和修改后的值发送给后端,后端进行合并编译,生成css返回给前端,前端动态替换style标签的内容达到实时预览的效果。
主题列表页面
主题列表页面的主要功能是显示官方主题列表和显示自定义主题列表。
官方主题可进行的操作有预览和复制,不能修改,修改的话会自动生成新主题。自定义主题可以编辑和下载,及进行修改名称、复制、删除操作。
官方主题列表后端返回,数据结构如下:
{ name: '官方主题-1', // 主题名称 by: 'by hui', // 来源 description: '默认主题', // 描述 theme: { // 主题改动点列表 common: { '$--color-brand': '#e72528' } } }
自定义主题保存在localstorage里,数据结构如下:
{ name: name, // 主题名称 update: Date.now(), // 最后一次修改时间 theme: { // 主题改动点列表 common: { //... } } }
复制主题即把要复制的主题的theme.common数据复制到新主题上即可。
需要注意的就是新建主题时要判断主题名称是否重复,因为数据结构里并没有类似id的字段。另外还有一个小问题是当预览官方主题时修改的话会自动生成新主题,所以还需要自动生成可用的主题名,实现如下:
const USER_THEME_NAME_PREFIX = '自定义主题-'; function getNextUserThemeName() { let index = 1 // 获取已经存在的自定义主题列表 let list = getUserThemesFromStore() let name = USER_THEME_NAME_PREFIX + index let exist = () => { return list.some((item) => { return item.name === name }) } // 循环检测主题名称是否重复 while (exist()) { index++ name = USER_THEME_NAME_PREFIX + index } return name }
界面效果如下:
因为涉及到几个页面及不同组件间的互相通信,所以vuex是必须要使用的,vuex的state要存储的内容如下:
const state = { // 官方主题列表 officialThemeList: [], // 自定义主题列表 themeList: [], // 当前编辑中的主题id editingTheme: null, // 当前编辑的变量类型 editingActionType: 'Color', // 可编辑的变量列表数据 variableList: [], // 操作历史数据 historyIndex: 0, themeHistoryList: [], variableHistoryList: [] }
editingTheme是代表当前正在编辑的名字,主题编辑时依靠这个值来修改对应主题的数据,这个值也会在localstorage里存一份。
editingActionType是代表当前正在编辑中的变量所属组件类型,主要作用是在切换要修改的组件类型后预览列表滚动到对应的组件位置及用来渲染对应主题变量对应的编辑控件,如下:
页面在vue实例化前先获取官方主题、自定义主题、最后一次编辑的主题名称,设置到vuex的store里。
编辑预览页面
编辑预览页面主要分两部分,左侧是组件列表,右侧是编辑区域,界面效果如下:
组件预览区域
组件预览区域很简单,无脑罗列出所有组件库里的组件,就像这样: