一行命令搞定项目多套主题及图片色系的切换
项目基于vue-cli3.0
需求: 项目需要配置多套主题色系,且相应的图片也需要改变,希望做到动态切换。
思考: 面对这种需求,可能之前大多都会用多套CSS和图片资源来完成,之前我们确实也有考虑过这种方式,将多套CSS及图片资源通过Nginx来配置切换,但这样仍然是会有多套资源, 与我所想的只用一套资源有所出入。其实细想下来,CSS只用一套倒好解决(用scss变量便行),只是图片更换稍微复杂点。针对这个问题,我最后选择了SVG,以下是具体的实现方式。
CSS切换
实现思路:使用scss变量,将样式配置写成js文件,借助webpack打包将js转换为scss并应用至项目。
为了项目结构清晰,新建themes文件夹用于主题配置文件,目录下新建不同文件来区分不同主题色系。
variables-xx.js文件如下(格式为key -> value, 不同的js文件只需要改变对应key的value即可):
module.exports = {
'themeColor': {
'primary': '#FC1B10',
'basic-font-family': 'PingFangSC-Regular',
'step-bar-color': '#C9C9C9',
'step-line-color': '#E7E7E7',
'step-active-color': '#FC1B10',
'tip-color': '#FC1B10'
}
}
index.js文件如下:
/**
* 获取npm切换主题命令末尾参数,导入相应主题配置文件。
*/
const processArgv = process.argv
const theme = processArgv.pop()
let styleVariables = null
switch (theme) {
case 'xx':
styleVariables = require('./variables-xx')
break
case 'xxx':
styleVariables = require('./variables-xxx')
break
default:
styleVariables = require('./variables-xxxx')
break
}
module.exports = styleVariables['themeColor']
写好配置文件后,接下来就是将js转换成scss,并自动引入全局。
这一步我们使用webpack来完成: vue.config.js
const merge = require('webpack-merge')
/* 全局scss变量 */
const styleVariables = require('./src/themes/index')
module.exports = {
chainWebpack: config => {
config.module
.rule('style')
.test(/\.scss$/)
.use(['style-loader', 'css-loader?sourceMap&minimize'])
.loader('sass-loader')
.options(
merge({
data: '@import "./src/assets/scss/variables.scss";'
+ Object.keys(styleVariables)
.map(key => `\$${key}: ${styleVariables[key]};`)
.join('\n'),
sourceMap: true,
sourceMapContents: true
})
)
.end()
}
}
'@import "./src/assets/scss/variables.scss";' 这个文件是项目中的全局scss,由于配置中只改变了色系,因此其他的全局样式单独放在了variables.scss中。使用sass-loader完成转换。
此外,在package.json中再配置一行命令,以便根据命令行参数来切换。
# 开发
"serve:theme": "vue-cli-service serve --theme"
# 生产
"build:theme": "vue-cli-service build --theme"
使用如下:
npm run serve:theme xx
# xx 即为对应的variables-xx.js配置文件
SVG色系切换
这个方案得益于 花裤衩的这篇文章,文章写的很详细,因此以下只写实现方案,原理不再赘述。
src/components目录下编写SvgIcon组件:
点击查看代码
<template>
<svg :class="[svgClass, 'theme']" aria-hidden="true">
<use :xlink:href="svgName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconName: {
type: String,
required: true
},
/* 自定义样式 */
iconClass: {
type: String,
default: ''
}
},
computed: {
svgClass() {
if (this.iconClass) {
return `svg-icon ${this.iconClass}`
} else {
return 'svg-icon'
}
},
svgName() {
return `#icon-${this.iconName}`
}
}
}
</script>
<style lang="scss" scoped>
.svg-icon {
width: 100%;
height: 100%;
// vertical-align: -0.3em;
fill: currentColor;
color: #909399;
overflow: hidden;
}
.theme {
color: $primary;
}
</style>
src目录下新建icons文件夹:
icons/index.js文件如下
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
/* 全局注册SvgIcon组件 */
Vue.component('svg-icon', SvgIcon)
/**
* 获取相应目录下的svg文件
* 第二个参数:为是否查找子目录 (false为不查找)
*/
const req = require.context('./svg', false, /\.svg$/)
/* 将所有文件转换成webpack context module并放进数组供webpack svg-sprite-loader获取解析 */
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
接下来便要借助webpack了,主要是用到svg-sprite-loader,闲话少叙,上代码。 vue.config.js
module.exports = {
chainWebpack: config => {
/* set svg-sprite-loader */
config.module
.rule('svg')
.exclude
.add(path.join(__dirname, 'src/icons')) // 删除默认配置中处理svg
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include
.add(path.join(__dirname, 'src/icons')) // 处理svg目录
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]' // icons目录下,根据svg文件的文件名与symbolId一一对应。
})
.end()
}
}
组件中使用SvgIcon
<svg-icon icon-name="test-loading" />
svg相关的配置完成后,只需将svg文件放入 src/icons/svg 目录下,就能自动转换。
至此,我们已经完成了CSS及图片资源的动态切换,写好配置文件后,一行命令便能解决。