{"id":23111844,"url":"https://github.com/dext7r/vue-less-dynamic-theming","last_synced_at":"2026-01-15T22:56:58.047Z","repository":{"id":233980772,"uuid":"629781960","full_name":"dext7r/vue-less-dynamic-theming","owner":"dext7r","description":"vue-less-dynamic-theming","archived":false,"fork":false,"pushed_at":"2023-08-03T02:17:25.000Z","size":322,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-09T11:31:20.678Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://vue-less-dynamic-theming.h7ml.cn/","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dext7r.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-04-19T02:36:32.000Z","updated_at":"2024-04-15T09:38:53.000Z","dependencies_parsed_at":"2024-04-17T22:01:19.076Z","dependency_job_id":null,"html_url":"https://github.com/dext7r/vue-less-dynamic-theming","commit_stats":null,"previous_names":["dext7r/vue-less-dynamic-theming"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dext7r%2Fvue-less-dynamic-theming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dext7r%2Fvue-less-dynamic-theming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dext7r%2Fvue-less-dynamic-theming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dext7r%2Fvue-less-dynamic-theming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dext7r","download_url":"https://codeload.github.com/dext7r/vue-less-dynamic-theming/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247097972,"owners_count":20883127,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-17T02:11:28.863Z","updated_at":"2026-01-15T22:56:58.008Z","avatar_url":"https://github.com/dext7r.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 前言\r\n\r\n每个网站都会有自己的一个主题色，但是随着行业内卷，越来越多的网站为了凸显特点，也为了更加迎合用户的需求，推出网站换肤功能。用户可以自己选择网站的主题：比如黑夜主题等等，从而实现了个性化定制。\r\n\r\n市面上常见的换肤功能主要有以下 2 种：\r\n\r\n- 网站自带几套固定主题，用户只能选择有限的几个主题。\r\n- 主题色由用户随意更改，真正做到用户的个性化定制。\r\n\r\n## 1 常见前端换肤方案\r\n\r\n### 1.1 利用 class 命名空间\r\n\r\n优点：简单、容易理解，非常容易实现。\r\n\r\n缺点：需要定义 class、手动维护、容易混乱\r\n\r\n### 1.2 预备多套 CSS 主题（推荐）\r\n\r\n优点：非常好理解，一套主题定义一套 css\r\n\r\n缺点：需要手写多套 css 样式代码，且更换主题需要下载新的 css 样式代码。\r\n\r\n### 1.3 动态换肤\r\n\r\n原理：主要是基于 element-ui 换肤方案的实现，生成一套主题， 将主题配色配置写在 js 中，在浏览器中用 js 动态修改 style 标签覆盖原有的 CSS。\r\n\r\n优点：通过定义函数的形式自动替换、操作性较强\r\n\r\n缺点：需要有统一打包出来的 index.css，实现难度较高。\r\n\r\n### 1.4 less 在线编译实现\r\n\r\n原理：使用 `modifyVars()`方法, 基于 less 在浏览器中的编译来实现。在引入 less 文件的时候需要通过 link 方式引入，然后基于 less.js 中的方法来进行修改 less 变量。\r\n\r\n特点：编译速度依赖客户端性能、切换不及时，运行时编译、需要额外引入 less.main.js、样式文件通过 link 方式引入。\r\n\r\n这种方式一般不推荐。\r\n\r\n### 1.5 CSS 变量换肤（推荐）\r\n\r\n优点：只需一套 CSS 文件；换肤不需要延迟等候；对浏览器性能要求低；可自动适配多种主题色\r\n\r\n缺点：不兼容 IE\r\n\r\n## 2 要实现的需求\r\n\r\n针对市场上常见的主题换肤方案，最符合用户个性化定制的方案无疑是让用户自定义主题色，实现热换肤。\r\n\r\n**最终需求：**\r\n\r\n1.  默认有几套主题色供用户选择\r\n2.  用户也可以自定义主题色\r\n3.  用户选定主题后需立即生效，无需重启项目或重新打包项目\r\n\r\n## 3 采用的方案\r\n\r\n为了满足上面的几种需求，且实现起来简单 一点，我们最终采用了 less+css 变量结合的方式来实行热换肤。因为我们需要热换肤，所以像 iview 这类的 UI 组件库提供的主题则不考虑，因为它们每次更换主题后需要重启项目才行。\r\n\r\n**准备工作：**\r\n\r\n- Vue 项目\r\n- 安装 less\r\n\r\n## 4 具体实现\r\n\r\n### 4.1 初始化 vue 项目\r\n\r\n任意初始化一个 Vue 项目，当然你也可以在已有的项目里面更改：\r\n\r\n![](https://pic1.zhimg.com/v2-04dd507ae98979eecb3294251c831ca8_b.jpg)\r\n\r\n### 4.2 安装必要插件\r\n\r\n在这里我们会用到两个样式处理插件，项目执行以下命令：\r\n\r\n```js\r\nnpm install style-resources-loader -D\r\nnpm install vue-cli-plugin-style-resources-loader -D\r\n```\r\n\r\n为了让我们的 Vue 项目里面能够使用 less，还需要安装 less 相关插件：\r\n\r\n```text\r\nnpm install less-loader@5.0.0 --save\r\nnpm install less --save\r\n```\r\n\r\nVue 项目如何配置 less 这里不做过多的介绍，因为我们的重点不在这里，我们的最终目的就是在项目里面能够使用 less。\r\n\r\n### 4.3 新建 style.less\r\n\r\nstyle.less 用于配置全局的默认样式，也可以是默认主题或字体颜色。在项目 src 目录下新建 theme 文件夹，然后新建 style.less，代码如下：\r\n\r\n```text\r\n/src/theme/style.less\r\n\r\n// 默认的主题颜色\r\n@primaryColor: var(--primaryColor, #000);\r\n@primaryTextColor: var(--primaryTextColor, green);\r\n// 导出变量\r\n:export {\r\n  name: \"less\";\r\n  primaryColor: @primaryColor;\r\n  primaryTextColor: @primaryTextColor;\r\n}\r\n```\r\n\r\n### 4.4 配置 vue.config.js\r\n\r\n在项目根目录新建 vue.config.js 文件，编写配置，代码如下：\r\n\r\n```js\r\nconst path = require('path')\r\nmodule.exports = {\r\n  pluginOptions: {\r\n    'style-resources-loader': {\r\n      preProcessor: 'less',\r\n      patterns: [\r\n        // 这个是加上自己的路径,不能使用(如下:alias)中配置的别名路径\r\n        path.resolve(__dirname, './src/theme/style.less'),\r\n      ],\r\n    },\r\n  },\r\n}\r\n```\r\n\r\n当我们配置好 vue.config.js 文件后，就可以在项目的任何地方使用我们预先定义的 less 变量了，示例代码如下：\r\n\r\n```text\r\n\u003cstyle lang=\"less\" scoped\u003e\r\np {\r\n    color: @primaryTextColor;\r\n}\r\n\u003c/style\u003e\r\n```\r\n\r\n我们随意更改一下我们的 Vue 项目：\r\n\r\n修改 HelloWorld 组件，在组件中使用 less 语法以及刚刚我们定义的全局变量。\r\n\r\n组件代码如下：\r\n\r\n```js\r\n\u003ctemplate\u003e\r\n  \u003cdiv class=\"hello\"\u003e\r\n    \u003cp\u003e我是测试文字\u003c/p\u003e\r\n  \u003c/div\u003e\r\n\u003c/template\u003e\r\n\r\n\u003cscript\u003e\r\nexport default {\r\n  name: \"HelloWorld\",\r\n};\r\n\u003c/script\u003e\r\n\u003cstyle scoped lang=\"less\"\u003e\r\n.hello {\r\n  p {\r\n    color: @primaryTextColor;\r\n  }\r\n}\r\n\u003c/style\u003e\r\n```\r\n\r\n此时的文字颜色便是我们刚刚设置的绿色了。\r\n\r\n![](https://pic1.zhimg.com/v2-65eb9150d0598b3a6a33352023c5e354_b.jpg)\r\n\r\n### 4.5 配置几套可选主题\r\n\r\n在/src/theme 目录下新建 model.js，编写自定义主题代码，代码如下：\r\n\r\n```js\r\n// 一套默认主题以及一套暗黑主题\r\n// 一套默认主题以及一套暗黑主题\r\nexport const themes = {\r\n  default: {\r\n    primaryColor: `${74}, ${144},${226}`,\r\n    primaryTextColor: `${74}, ${144},${226}`,\r\n  },\r\n  dark: {\r\n    primaryColor: `${0},${0},${0}`,\r\n    primaryTextColor: `${0},${0},${0}`,\r\n  },\r\n}\r\n```\r\n\r\n这里我们定义了两套默认主题，目的就是为了让用户能够在这两套主题中切换，其中主题颜色我们采用了 rgba 的写法，因为有可能我们需要在不用的地方使用同一主题色，但是透明度不一样。\r\n\r\n### 4.6 编写修改主题的方法\r\n\r\n全局的颜色变量以及两套默认主题我们都编写好了，在这里我们已经实现了静态更改主题，即可以更改颜色，项目重新运行后便可生效。但是这还达不到我们的目的，我们需要动态更改主题，所以我们还需要编写一个能够动态更改主题的方法。\r\n\r\n在/src/theme 文件夹下新建 theme.js 文件，代码如下：\r\n\r\n```js\r\nimport { themes } from './model'\r\n// 修改页面中的样式变量值\r\nconst changeStyle = (obj) =\u003e {\r\n  for (let key in obj) {\r\n    document.getElementsByTagName('body')[0].style.setProperty(`--${key}`, obj[key])\r\n  }\r\n}\r\n// 改变主题的方法\r\nexport const setTheme = (themeName) =\u003e {\r\n  localStorage.setItem('theme', themeName) // 保存主题到本地，下次进入使用该主题\r\n  const themeConfig = themes[themeName]\r\n  // 如果有主题名称，那么则采用我们定义的主题\r\n  if (themeConfig) {\r\n    localStorage.setItem('primaryColor', themeConfig.primaryColor) // 保存主题色到本地\r\n    localStorage.setItem('primaryTextColor', themeConfig.primaryTextColor) // 保存文字颜色到本地\r\n    changeStyle(themeConfig) // 改变样式\r\n  } else {\r\n    let themeConfig = {\r\n      primaryColor: localStorage.getItem('primaryColor'),\r\n      primaryTextColor: localStorage.getItem('primaryTextColor'),\r\n    }\r\n    changeStyle(themeConfig)\r\n  }\r\n}\r\n```\r\n\r\n这里我们编写了两个方法，一个是更改全局 css 变量值的方法，达到更改样式的作用，另一个是更改主题的方法，可以让用户选择我们准备的几套主题或者自定义颜色。\r\n\r\n### 4.7 动态变换主题\r\n\r\n修改我们的 HelloWorld 组件，演示如何动态修改主题。\r\n\r\n测试代码如下：\r\n\r\n```js\r\n\u003ctemplate\u003e\r\n  \u003cdiv class=\"hello\"\u003e\r\n    \u003cdiv class=\"box-1\"\u003e\u003c/div\u003e\r\n    \u003cdiv class=\"box-2\"\u003e\u003c/div\u003e\r\n    \u003cp\u003e我是测试文字\u003c/p\u003e\r\n    \u003cbutton @click=\"defaultTheme\"\u003e默认主题\u003c/button\u003e\r\n    \u003cbutton @click=\"dark\"\u003e暗黑主题\u003c/button\u003e\r\n    \u003cbutton @click=\"custom\"\u003e自定义主题\u003c/button\u003e\r\n  \u003c/div\u003e\r\n\u003c/template\u003e\r\n\r\n\r\n\u003cscript\u003e\r\nimport { setTheme } from \"../theme/theme\";\r\nexport default {\r\n  name: \"HelloWorld\",\r\n  mounted() {\r\n    this.init(); // 初始化主题\r\n  },\r\n  methods: {\r\n    init() {\r\n      setTheme(\"default\"); // 初始化未默认主题\r\n    },\r\n    // 更改为默认主题\r\n    defaultTheme() {\r\n      setTheme(\"default\");\r\n    },\r\n    // 更改为暗黑主题\r\n    dark() {\r\n      setTheme(\"dark\");\r\n    },\r\n    // 更改为自定义主题\r\n    custom() {\r\n      let newColor = {\r\n        r: 12,\r\n        g: 33,\r\n        b: 234,\r\n      };\r\n      let newPrimaryColor = `${newColor.r},${newColor.g},${newColor.b}`;\r\n      localStorage.setItem(\"primaryColor\", newPrimaryColor); // 将新的主题色存入本地\r\n      setTheme();\r\n    },\r\n  },\r\n};\r\n\u003c/script\u003e\r\n\u003cstyle scoped lang=\"less\"\u003e\r\n.hello {\r\n  display: flex;\r\n  flex-direction: column;\r\n  align-items: center;\r\n  .box-1 {\r\n    width: 50px;\r\n    height: 50px;\r\n    margin-bottom: 30px;\r\n    background: rgba(@primaryColor, 1);\r\n  }\r\n  .box-2 {\r\n    width: 50px;\r\n    height: 50px;\r\n    margin-bottom: 30px;\r\n    background: rgba(@primaryColor, 0.3);\r\n  }\r\n  p {\r\n    color: @primaryTextColor;\r\n  }\r\n}\r\n\u003c/style\u003e\r\n```\r\n\r\n当我们进入页面是，会采用默认主题样式，然后用户可以点击按钮更改自定义的样式，并且会保存到本地。\r\n\r\n![](https://pic2.zhimg.com/v2-7434d7dfa8240337b86a72b5583d08b1_b.jpg)\r\n\r\n## 5 总结\r\n\r\n利用 less 和 css 变量动态修改主题，我们主要新建了 3 个样式文件，作用分别是默认主题、自定义的几套主题以及修改主题的工具函数。本篇文章只是一个简单的入门，通常自定义主题我们会提供给用户颜色选择面板，大家可以结合使用。\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdext7r%2Fvue-less-dynamic-theming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdext7r%2Fvue-less-dynamic-theming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdext7r%2Fvue-less-dynamic-theming/lists"}