weixin_39548193
2020-11-21 19:39 阅读 0

欢迎讨论更加优雅的主题切换方式

最近因为某些原因,终于把一直纠结的主题方式及切换方式搞了出来。

设计思路

我能想到的主题切换方式无非两种:

  • 一种是在body中加样式,对所有css进行命名空间(css前缀)处理
  • 一种是类似Element自身的主题切换方式,通过编写scss改变主题

前一种方式在花裤衩同学的vue-element-admin中有具体的实现,项目主页:https://github.com/PanJiaChen/vue-element-admin 他是先将element自己提供的变量文件修改后生成新的css,然后将该css用gulp进行二次编辑增加命名空间,然后引入。也就是说他只是修改了UI框架的主题,并没有修改自己写的样式主题。

第二种方式则是element自己生成一个sass变量文件,执行命令行后生成新主题,然后让开发者自己导入。

以上两种其实都并没有实现项目本身自定义样式的主题切换,所以就产生了很多的问题。但仔细分析一下,其实我们只要将自己写的css样式也如同element官方那样打包一遍,然后动态引入就好了嘛。当然也可能是我傻,没想到还有其他方法。

介于以上总结,项目中设计的主题加载和切换方式大致流程如下: 1. 页面中仅引入基本样式 2. 页面初始化完成,动态加载主题文件 3. 用户切换主题:判断主题文件是否已经存在,存在则不操作;不存在则清空所有的主题文件,然后动态加载对应的主题文件。

主题创建

当前主题编写参考了Element的主题切换方式,通过sass进行变量修改。


# 复制基础变量文件 (src/assets/css/theme/default.scss),重命名为 blue.scss 并修改内部变量
# 在 src/assets/css 目录中创建新的主题入口文件 theme-blue.scss,并做如下引入

// 引入新主题变量文件
 './theme/blue'
// 引入默认主题
 './theme-default'

如此简单的便可以完成主题创建。前文说过我们需要动态引入文件,而动态引入我采用的是js读取文件路径并加载的形式,也就意味着该文件必须存在实际的物理地址,所以你需要先对项目进行打包,生成对应的静态的css文件,而不是scss文件。为了达成这个目的我们还需要对webpack进行设置。沿着这个思路我们还需要做以下工作。

配置webpack

在webpack.prod.conf.js中对入口变量做出修改,注意是prod.js


// 增加新的入口,并赋予文件名和路径
entry: {
    'theme-default': './src/assets/style/theme-default.scss'
}

// 对HtmlWebpackPlugin插件增加忽略chunk设置,防止该chunk被加入到生成后的html中
new HtmlWebpackPlugin({
    excludeChunks: ['theme-default']
})

配置供js使用的主题文件路径

配置好新的入口后,我们对项目进行打包,等待编译完成。当打包结束后我们应该能够看到在dist\static\css下有新增的theme文件,这个文件就是我们的主题文件了。接下来则是为主题切换做基础。我们需要把生成的文件路径提供给js。但是要意识到,该文件在开发时是没有的,只有在打包完成后才有,所以我们需要另一个操作:改写打包文件。

在打包文件生成后,我们需要利用node循环读取dist\static\css下的所有文件,并把所有的theme文件路径输出到html中,当然你也可以输出到公共js中,比如app.js。这里具体的实现方法请看build\themeExtract.js

写完文件后我们只需要执行它就可以了。当然了,把它直接和 npm run build 绑定到一起不是更好吗?所以在 package.json中做如下修改即可


"scripts": {
    "dev": "node build/dev-server.js",
    "start": "node build/dev-server.js",
    // &&代表顺序执行
    "build": "node build/build.js && node build/themeExtract.js"
}

执行后,我们再看看打包后的html文件,在head部分能看到一个全局的变量themeURL,里面就是我们需要的所有的主题文件路径了。

在本篇文章发表时,本项目master分支并没有以上说的所有信息,想看的请clone本项目然后切换到dev分支

使用及调试

在上个步骤中我们已经有了全局变量themeURL,那么只需要对主题文件进行匹配即可。当更换主题时,我们先查找已经加载的主题,如果没有则直接插入主题文件,如果有则清空所有主题文件,然后再插入。至于动态插入文件的方法,项目中已对该方法进行封装,可在 src\util\changeTheme.js中查看。


# 使用主题切换的方法示例,需要保证changeTheme()中的参数与主题文件名保持一致
 changeTheme from '@/util/changeTheme'

export default {
    loadTheme(theme){
        changeTheme(theme)
    }
}

因为主题文件是打包后才生成的,所以在开发环境中,主题切换并不好用,只能通过手动修改sass主题文件的方式来做切换了,这也是目前该方案最大的问题。

至于调试动态载入主题js是否成功.....唔,你先打包一份,然后把打包后的多出来的变量复制到 src\index.html 中,再把打包出来的文件放到和src同级的static目录中就可以了嘛...

优势及问题

这种切换方式能够最大程度上避免CSS污染,页面也更加干净,不会产生多余的文件。 如果使用偷懒的方法,将所有自己写的样式都放到主题中(比如我),那么后期维护也异常的简单,改改变量值就可以了嘛。 如果使用繁琐的方法,抽出所有样式中的主题部分(如背景色、文字、颜色、图片等),也能极大减少主题文件的大小,就是后期维护的时候会同时看两个文件才能知道这里真正的样式是什么,而且在编辑时容易丢东西。

该提问来源于开源项目:harsima/vue-backend

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

5条回答 默认 最新

  • weixin_39770311 weixin_39770311 2020-11-21 19:39

    您好,请问您的博客除了CSDN上,还有其他写文章的地方么? 还有,可以留个二维码,方便赞助啥的。

    点赞 评论 复制链接分享
  • weixin_39548193 weixin_39548193 2020-11-21 19:39

    暂时没在其他网站上开设博客,仅有的计划是在segmentfault上再开一个账户,csdn上感觉和前端不太合(个人感觉)。暂时不用赞助,而且没到用赞助的地步,而且这个项目目前更适合学习,并不推荐使用在生产环境中,赞助什么的感觉太过了。谢谢你的心意了

    点赞 评论 复制链接分享
  • weixin_39548193 weixin_39548193 2020-11-21 19:39

    讨论其他问题请再开issue,保证主题内容干净

    点赞 评论 复制链接分享
  • weixin_39614322 weixin_39614322 2020-11-21 19:39

    很有启发,最近也正在看主题换肤的问题。整个方案,你提到的一个痛点,在开发阶段怎么切换主题?我的想法是,这个是不是可以在开发阶段去输入一个主题的变量,然后,在js代码里面去根据这个变量动态引入主题样式文件?

    点赞 评论 复制链接分享
  • weixin_39614322 weixin_39614322 2020-11-21 19:39

    不过好像开发阶段动态引scss文件是个问题

    点赞 评论 复制链接分享

相关推荐