likes
comments
collection
share

vue内引入less,实时切换组件样式

作者站长头像
站长
· 阅读数 72

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

前言

vue内部,编写less文件,如何实现主题实时切换的功能呢?有一点要注意的是,在vue某个文件内,通过import方式引入less npm包,再less.modifyVars是无法实时修改主题的,因为此时的less文件已编译完毕,无法触发webpack的实时更新。

要想实时切换主题,只能将less文件放在public目录下,但此时涉及到一个问题,组件内部less文件那么多,难道每次修改less文件,都得手动再在public目录下的color.less里改吗?有没有一种自动打包目录下的所有less文件,并将其生成一个color.less,放在public目录下的功能吗?

以上问题,都能得到解决,请看下面内容

内容

项目下面组件的less目录如下图所示: vue内引入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)
	    }
  }
};

点击该按钮,可实时切换主题样式

转载自:https://juejin.cn/post/7034705244001042446
评论
请登录