前端换肤方案

497 阅读3分钟

PC端vue方案(antd-vue 1.x)

前言

react项目的antd因为使用了CSS-IN-JS,直接改变主题变量即可。vue的老项目antd版本较低,不支持CSS-IN-JS,所以换肤操作相对比较复杂。

方案

  1. 安装 webpack-theme-color-replacer
yarn add webpack-theme-color-replacer -S
  1. 封装webpack 插件,新建theme-color-replacer.js
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default

const getAntdSerials = (color) => {
  // 淡化(即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('#', '')).join(',')
  return lightens.concat(colorPalettes).concat(rgb)
}

const themePluginOption = {
  fileName: 'css/theme-colors-[contenthash:8].css',
  matchColors: getAntdSerials('#1890ff'), // 默认主色
  // 改变样式选择器,解决样式覆盖问题
  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
      // fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
      case '.ant-steps-item-process .ant-steps-item-icon':
        return ':not(.ant-steps-item-custom)' + 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
  1. webpack 的配置 (vue.config.js)
// 动态修改主题色配置
const createThemeColorReplacerPlugin = require('./xxx/theme-color-replacer')
module.exports = {
  // ..... other config
  plugins: [
    createThemeColorReplacerPlugin() // webpack plugins
  ]
}
  1. 封装公共方法
// themeColor.js
import client from 'webpack-theme-color-replacer/client'
import generate from '@ant-design/colors/lib/generate'

export default {
  // 运行时更改主题颜色
  changeColor (newColor) {
    var options = {
      newColors: this.getAntdSerials(newColor), // 新颜色数组,与" 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)
  }
}
  1. 使用
import themeColor from './themeColor.js'
//...
themeColor.changeColor('#123456').finally(() => {
    // 切换成功后回调方法 这里可以关闭loading 或者 提示等等
})
  1. node高版本启动时遇到报错的话,参考修改package.json
"scripts": {
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
  "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"
  }

非antd样式处理

  1. 安装style-resources-loader
yarn add style-resources-loader -D
  1. vue.config.js增加代码,需要在modifyVars中定义默认值,否则会识别不到
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          'primary-color': '#3D92FF',
        }
      }
    }
  },
  // ..... other config
  pluginOptions: {
    'style-resources-loader': {
      preProcessor: 'less',
      patterns: [path.resolve(__dirname, 'node_modules/ant-design-vue/es/style/themes/default.less')]
    }
  }
}
  1. 直接使用变量写样式
<style scoped lang="less">
.test{
  color: @primary-color;
}
</style>

小程序方案(uni-app,vue)

参考ext.dcloud.net.cn/plugin?id=6…

  1. 每个页面最外层view上绑定:style="skin"
  2. skin存放在vuex中,值:--theme-color:#ffffff;
  3. 样式直接使用变量
<style lang="less">
page {
   background-color: var(--theme-color);
}
 <style />

react方案(vite,antd5.x)

  1. antd组件直接用ConfigProvider的theme配置主题

  2. 非组件样式

    1. vite.config.js
    2. css: {
        // css预处理器
        preprocessorOptions: {
          less: {
            // 自定义变量文件
            additionalData: '@import "./src/styles/theme.less";'
          },
        },
      },
      
    3. theme.less中定义样式变量,页面less文件直接使用变量即可
    4. @primary-color: var(--theme-color);
      
    5. 动态在html根节点设置--theme-color的值