前言
vue内部,编写less文件,如何实现主题实时切换的功能呢?有一点要注意的是,在vue某个文件内,通过import方式引入less npm包,再less.modifyVars是无法实时修改主题的,因为此时的less文件已编译完毕,无法触发webpack的实时更新。
要想实时切换主题,只能将less文件放在public目录下,但此时涉及到一个问题,组件内部less文件那么多,难道每次修改less文件,都得手动再在public目录下的color.less里改吗?有没有一种自动打包目录下的所有less文件,并将其生成一个color.less,放在public目录下的功能吗?
以上问题,都能得到解决,请看下面内容
内容
项目下面组件的less目录如下图所示:
现在需要将这些styles下面的less文件,自动打包成一个color.less,并将其放在public目录下,generateTheme.js
功能代码如下所示:
const fs = require("fs");
const path = require("path");
async function generateTheme({
outputFilePath
}) {
try {
let filePath = path.join(__dirname, './src/styles/index.less');
let css = "";
css = combineLess(filePath);
css = minifyCss(css);
if (outputFilePath) {
fs.writeFileSync(outputFilePath, css);
console.log(
`🌈 Theme generated successfully. OutputFile: ${outputFilePath}`
);
} else {
console.log("Theme generated successfully");
}
} catch (error) {
console.log("error", error);
}
}
function minifyCss(css) {
// Removed all comments and empty lines
css = css
.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "")
.replace(/^\s*$(?:\r\n?|\n)/gm, "");
/*
Converts from
.abc,
.def {
color: red;
background: blue;
border: grey;
}
to
.abc,
.def {color: red;
background: blue;
border: grey;
}
*/
css = css.replace(/\{(\r\n?|\n)\s+/g, "{");
/*
Converts from
.abc,
.def {color: red;
}
to
.abc,
.def {color: red;
background: blue;
border: grey;}
*/
css = css.replace(/;(\r\n?|\n)\}/g, ";}");
/*
Converts from
.abc,
.def {color: red;
background: blue;
border: grey;}
to
.abc,
.def {color: red;background: blue;border: grey;}
*/
css = css.replace(/;(\r\n?|\n)\s+/g, ";");
/*
Converts from
.abc,
.def {color: red;background: blue;border: grey;}
to
.abc, .def {color: red;background: blue;border: grey;}
*/
css = css.replace(/,(\r\n?|\n)[.]/g, ", .");
return css;
}
function combineLess(filePath) {
const fileContent = fs.readFileSync(filePath).toString();
const directory = path.dirname(filePath);
return fileContent
.split("\n")
.map((line) => {
if (line.startsWith("@import")) {
let importPath = line.match(/@import\ ["'](.*)["'];/)[1];
if (!importPath.endsWith(".less")) {
importPath += ".less";
}
let newPath = path.join(directory, importPath);
return combineLess(newPath);
}
return line;
})
.join("\n");
}
// export default generateTheme
generateTheme({
outputFilePath: path.join(__dirname,'../../public/color.less')
});
运行node命令node generateTheme.js
即可自动在public目录下生成color.less
文件
在以上utils
文件夹下,有个setting.js
文件,该文件将color.less
以及less.min.js
自动引入至html内,代码如下所示:
import { message } from 'ant-design-vue/es'
let lessNodesAppended
const themeList = [
{
key: '深蓝', bg: '#193a81'
},
{
key: '淡黑', bg: 'rgba(68,68,68,.8)'
}
]
const updateTheme = (theme = '') => {
// Don't compile less in production!
/* if (process.env.NODE_ENV === 'production') {
return;
} */
// Determine if the component is remounted
if (!theme) {
return
}
const matchedItem = themeList.filter(a => a.key === theme);
if (!matchedItem.length) {
message.error(`组件库内并无该主题${theme},请查看文档再选择主题种类`);
return;
}
const hideMessage = message.loading('正在编译主题!', 0)
console.info(`正在编译主题!`)
function buildIt() {
// 正确的判定less是否已经加载less.modifyVars可用
if (!window.less || !window.less.modifyVars) {
return
}
// less.modifyVars可用
window.less.modifyVars({
'@background-color': matchedItem[0].bg,
})
.then(() => {
hideMessage()
})
.catch(() => {
message.error('Failed to update theme')
hideMessage()
})
}
if (!lessNodesAppended) {
// insert less.js and color.less
const lessStyleNode = document.createElement('link')
const lessConfigNode = document.createElement('script')
const lessScriptNode = document.createElement('script')
lessStyleNode.setAttribute('rel', 'stylesheet/less')
lessStyleNode.setAttribute('href', '/color.less')
lessConfigNode.innerHTML = `
window.less = {
async: true,
env: 'production',
javascriptEnabled: true
};
`
lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js'
lessScriptNode.async = true
lessScriptNode.onload = () => {
buildIt()
lessScriptNode.onload = null
}
document.body.appendChild(lessStyleNode)
document.body.appendChild(lessConfigNode)
document.body.appendChild(lessScriptNode)
lessNodesAppended = true
} else {
buildIt()
}
}
export { updateTheme, themeList }
在App.vue
文件内部,引入该setting.js
文件
<template>
<a-button @click="clickMe" style="position: fixed;top:20px;right:20px;z-index:9999">clickMe</a-button>
...
</template>
<script>
import { updateTheme } from '@/sf-view-vue/src/utils/setting'
export default {
data() {
return {
theme: '深蓝',
};
},
methods: {
clickMe() {
this.theme === "深蓝" ? this.theme = "淡黑" : this.theme = "深蓝"
updateTheme(this.theme)
}
}
};
点击该按钮,可实时切换主题样式