Vue project online dynamic switch theme color; Tried a lot of methods, such as: server-side rendering judgment to return the corresponding theme CSS, or dynamically according to the theme to load the CSS corresponding files prepared in advance, and is server-side rendering;
Finally, I learned the way they used to switch themes while watching Ant Desgin Vue Pro, which felt quite good personally. Using the webpack-theme-color-replacer plug-in, which extracts theme color styles (e.g. Antd, Element-UI theme colors) from all output CSS files, And make a ‘theme-colors.css’ file that contains only the color styles. When your web page is running, the client section will help you download this CSS file, and then dynamically replace the colors with new custom colors.
1. Install webpack – theme – color – replacer;
npm i -S webpack-theme-color-replacer
Copy the code
2. Wrap generation for the WebPack configuration method
// theme-color-replacer.plugin.config.js
// Ant Desgin vue configuration
/ / introduce webpack - theme - color - replacer
const ThemeColorReplacer = require('webpack-theme-color-replacer')
// Introduce the @ant-design method to get the color value based on the incoming color transform
const generate = require('@ant-design/colors/lib/generate').default
// Introduce the default theme configuration. Here is ANTDV
const ThemeObj = require('./theme')
const getAntdSerials = (color) = > {
// Dilute (less tint)
const lightens = new Array(9).fill().map((t, i) = > {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)})const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace(The '#'.' ')).join(', ')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css'.// Output CSS file names by contenthash and hash
matchColors: getAntdSerials(ThemeObj['primary-color']), // The default theme color is required for the main color series. When switching, the color is obtained according to the color value configured here
// Change the style selector to solve the style overwriting problem
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item -selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>. ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () = > new ThemeColorReplacer(themePluginOption)
module.exports = createThemeColorReplacerPlugin
Copy the code
3. New theme-related color generation and replacement methods
// themeColor.js
import client from 'webpack-theme-color-replacer/client'
import generate from '@ant-design/colors/lib/generate'
export default {
getAntdSerials (color) {
// Dilute (less tint)
const lightens = new Array(9).fill().map((t, i) = > {
return client.varyColor.lighten(color, i / 10)})const colorPalettes = generate(color)
const rgb = client.varyColor.toNum3(color.replace(The '#'.' ')).join(', ')
return lightens.concat(colorPalettes).concat(rgb)
},
// Change the theme color at runtime
changeColor (newColor) {
var options = {
newColors: this.getAntdSerials(newColor), // New array of colors, corresponding to "matchColors"
changeUrl (cssUrl) {
return ` /${cssUrl}` // while router is not `hash` mode, it needs absolute path
}
// appendToEl: 'head', //optional. The element selector for appending child with `<style>`, default is 'body'. Using `appendToEl: 'body'` can make the css priority higher than any css in <head>
}
return client.changer.changeColor(options, Promise)}}Copy the code
4. The user clicks the color related method
import themeColor from './themeColor.js'
// color Array
const colorList = [
{
name: 'red'.color: '#F55448'
},
{
name: 'blue'.color: '#2783FE'}]// Update the theme method
const updateTheme = newPrimaryColor= > {
// Toggle loading, prompt, etc
themeColor.changeColor(newPrimaryColor).finally(() = > {
// You can disable loading, prompt, etc})}export { updateTheme, colorList }
Copy the code
settingTheme.vue
<template>
<a-tooltip
v-for="(item, index) in colorList"
:key="index"
class="setting-drawer-theme-color-colorBlock"
>
<template slot="title">
{{ item.key }}
</template>
<a-tag
:color="item.color"
@click="changeColor(item.color)"
>
<a-icon
v-if="item.color === primaryColor"
type="check"
/>
</a-tag>
</a-tooltip>
</template>
<script>
import { updateTheme, colorList } from './settingConfig'
export default {
data () {
return {
colorList
}
},
methods: {
changeColor (color) {
updateTheme(color)
}
}
}
</script>
Copy the code
5. Webpack configuration (vue.config.js)
// Dynamically modify the theme color configuration
const createThemeColorReplacerPlugin = require('./config/theme-color-replacer.plugin.config')
module.exports = {
/ /... other config
plugins: [
createThemeColorReplacerPlugin() // webpack plugins]}Copy the code
We’re done here
Element UI toggles themes to view