本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-02-19
export default defineConfig({
css: {
preprocessorOptions: {
less: {
globalVars: {
hack: `true; @import (reference) "${path.resolve('src/styles/vars.less')}";`
},
javascriptEnabled: true
}
}
}
})
相当于使用增加全局变量的方式,加入了这样一段话:
@hack: true; @import (reference) "src/styles/vars.less";
相当于import进来一个变量文件。
所以这里的hack属性可以用任意变量和属性来代替,比如
theme: "red; @import (reference) 'src/styles/vars.less';"
而 @import (reference)
代表着less中import导入时的采用 reference 选项,使用文件,但不会输出其内容(即,文件作为样式库使用)。
思路是一样的,通过在在每个less文件中添加import语句。
// my-plugin.ts
export default function myPlugin(options: any): any {
return {
name: 'transform-file',
enforce: 'pre',
// apply: 'build',
transform(src, id): any {
if (id.endsWith('less')) {
const code = `@import '${options.varsFile}'; ${src}`;
return {
code,
};
}
return undefined;
},
};
}
在 vite.config.js
中这样使用:
import { defineConfig } from 'vite';
import myPlugin from './my-plugin';
export default defineConfig({
plugins: [vue(), myPlugin({
varsFile: './src/styles/vars.less',
})],
});
基本样式文件
// style.less
div {
color: @c1;
}
主题样式文件
// ./themes/red.less
@import (multiple) './style.less';
@c1: red;
// ./themes/blue.less
@import (multiple) './style.less';
@c1: blue;
需要注意的是,使用 @import
需要增加 multiple
选项,否则只会编译出一份style的样式,下面这样生成多套主题时就会有问题了。
// main.less
.body-red {
@import './themes/red.less';
};
.body-blue {
@import './themes/blue.less';
};
编译 lessc main.less main.css
就会得到多套主题的样式如下:
/* theme.css */
.body-red div {
color: red;
}
.body-blue div {
color: blue;
}
import type { Plugin, IndexHtmlTransformResult } from 'vite';
export interface MultiThemeOptions {
/** 多主题变量所在目录 */
dir: string;
/** 需要打包哪些主题 */
themes: string[];
}
/**
* 如果有的组件完全没有使用到主题变量可以避免进行多皮肤代码转换 在less文件前采用注释/* no-theme-vars *\/
*/
export default function multiTheme(options: MultiThemeOptions): Plugin {
return {
name: 'multiTheme',
enforce: 'pre',
transform(src, id): any {
let code = '';
if (id.endsWith('less')) {
const { themes } = options;
if (src.trim().startsWith('/* no-theme-vars */') || !themes.length) {
return { code: src };
}
// 多套皮肤
if (themes.length > 1) {
themes.forEach((theme) => {
const sentence = `@import '${options.dir}${theme}.less';\n${src}`;
const template = `[theme=${theme}] {\n ${sentence}\n};\n\n`;
code += template;
});
} else if (themes.length === 1) { // 单套皮肤
const theme = themes[0];
code = `@import '${options.dir}${theme}.less';\n${src}`;
}
return {
code,
};
}
return undefined;
},
/**
* 为html增加对应的主题自定义属性
*/
transformIndexHtml(html: string): IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void> {
if (options.themes.length > 1) {
const reg = /<html(.*)>/g;
return html.replace(reg, `<html $1 theme=${options.themes[0]}>`);
}
return html;
},
};
}
export function setTheme(theme: string): void {
document.documentElement.setAttribute('theme', theme);
}
export function getTheme(): string {
return document.documentElement.getAttribute('theme') || '';
}
在vite.config.js
中这样使用:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import multiTheme from './my-plugin';
export default defineConfig({
plugins: [vue(), multiTheme({
dir: './src/styles/themes/', // 指定less样式变量文件所在的目录
themes: ['red', 'blue'], // 指定需要处理哪些主题样式
})],
});
该插件中transform会在文件加载完成后执行,由于设置enforce
为pre
,所以执行时机会在Vite核心插件在编译less之前,这个时候将less文件做更改,将源文件包括在对应主题选择器之下并引入对应的样式变量文件,具体如下:
div {
color: @c1;
}
span {
color: chocolate;
}
// 将上面的less代码转换成如下的less代码
[theme=red] {
@import './src/styles/red.less';
div {
color: @c1;
}
span {
color: chocolate;
}
};
[theme=blue] {
@import './src/styles/blue.less';
div {
color: @c1;
}
span {
color: chocolate;
}
};
之后,将更改后的less文件通过 { code: 'xxx' }
的形式交给后面的插件来处理,并有Vite核心插件帮我们完成编译,最后打包出来的css文件如下:
[theme=red] div[data-v-a6a41576] {
color: red
}
[theme=red] span[data-v-a6a41576] {
color: #d2691e
}
[theme=blue] div[data-v-a6a41576] {
color: #00f
}
[theme=blue] span[data-v-a6a41576] {
color: #d2691e
}
简易版,原理一样。
const loaderUtils = require('loader-utils');
module.exports = function(source, map) {
const options = loaderUtils.getOptions(this);
const { dir, themes } = options;
let code = '';
themes.forEach((theme) => {
const sentence = `@import '${dir}${theme}.less';\n${source}`;
const template = `[theme=${theme}] {\n ${sentence}\n};\n\n`;
code += template;
});
return code;
}
使用
module.exports = {
configureWebpack: {
resolveLoader: {
modules: ['node_modules','./my-loader/'],
},
},
chainWebpack: config => {
config.module
.rule('less')
.oneOf('vue')
.test(/\.less$/)
.use('my-less-loader')
.loader('my-less-loader')
.options({
dir: '/src/styles/themes/', // 指定less样式变量文件所在的目录
themes: ['default', 'red'], // 指定需要处理哪些主题样式
})
.after('less-loader')
},
}