一、为什么要自动注入 Less 全局变量
在大型前端项目中,我们通常会在 Less 中定义一些全局变量——颜色、字体、间距、断点等。比如:
// variables.less
@primary-color: #1890ff;
@font-size-base: 14px;
@border-radius: 6px;
@spacing-unit: 8px;但问题是:每个使用这些变量的 .less 文件都需要手动 @import:
// ❌ 每个组件都要写这一行
@import '../../styles/variables.less';
.my-component {
color: @primary-color;
}这就是重复劳动的源头。自动注入 Less 全局变量,就是在构建工具层面,自动在每个 Less 文件开头注入指定的变量文件,让所有组件文件都能直接使用这些变量,无需手动 import。
┌─────────────────────────────────────┐
│ 构建工具(Webpack/Vite) │
│ │
│ 编译每个 .less 文件时,自动在顶部追加: │
│ @import "variables.less"; │
│ │
│ 组件 A.less → 自动注入 → 正常编译 ✅ │
│ 组件 B.less → 自动注入 → 正常编译 ✅ │
│ 组件 C.less → 自动注入 → 正常编译 ✅ │
└─────────────────────────────────────┘二、Webpack 配置
2.1 less-loader v6 及以下:prependData
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
},
// ⬇️ 自动注入全局变量
prependData: `
@import "@/styles/variables.less";
@import "@/styles/mixins.less";
`,
},
},
],
},
],
},
};2.2 less-loader v7+: additionalData
less-loader v7 将 prependData 重命名为 additionalData,功能不变:
// webpack.config.js (less-loader v7+)
{
loader: 'less-loader',
options: {
additionalData: `
@import "@/styles/variables.less";
@import "@/styles/mixins.less";
`,
},
}2.3 配合 chainWebpack(Vue CLI 3/4)
// vue.config.js
module.exports = {
chainWebpack: config => {
const oneOfsMap = config.module.rule('less').oneOfs.store
oneOfsMap.forEach(item => {
item
.use('less-loader')
.loader('less-loader')
.tap(options => {
options.additionalData = `
@import "~@/styles/variables.less";
@import "~@/styles/mixins.less";
`
return options
})
})
},
}注意:Vue CLI 内部有多条 Less 规则(普通
.less、<style lang="less">等),需要遍历所有oneOf逐一注入。
三、Vite 配置
Vite 天生支持 CSS 预处理器,配置更简洁:
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
less: {
additionalData: `
@import "@/styles/variables.less";
@import "@/styles/mixins.less";
`,
javascriptEnabled: true,
},
},
},
})如果使用了 @ 别名,需要确保 Vite 的 resolve 别名已配置:
import { fileURLToPath, URL } from 'node:url'
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
css: {
preprocessorOptions: {
less: {
additionalData: `@import "@/styles/variables.less";`,
},
},
},
})Vite 中多文件注入
需要注入多个文件时,用分号或换行分隔:
additionalData: `
@import "@/styles/variables.less";
@import "@/styles/themes/default.less";
`⚠️ Vite 与 Webpack 的区别:Vite 的
additionalData只在编译 Less 文件时注入,不影响 JavaScript 中的 Less 变量引用。如果你在 JS 中动态使用 Less 变量(如less.modifyVars()),仍需另做处理。
四、Demo:主题系统实战
假设我们要构建一个支持深色/浅色主题切换的组件库,自动注入全局变量就是最优雅的方案。
4.1 目录结构
src/
├── styles/
│ ├── variables.less # 全局设计变量
│ ├── themes/
│ │ ├── light.less # 浅色主题覆盖
│ │ └── dark.less # 深色主题覆盖
│ └── mixins.less # 混入工具
├── components/
│ ├── Button/
│ │ ├── index.tsx
│ │ └── style.less # 直接使用变量,无需 import
│ └── Card/
│ ├── index.tsx
│ └── style.less4.2 全局变量文件
// styles/variables.less
// 颜色系统
@primary-color: #1890ff;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #ff4d4f;
// 中性色
@text-color: rgba(0, 0, 0, 0.85);
@text-color-secondary: rgba(0, 0, 0, 0.45);
@bg-color: #ffffff;
@bg-color-secondary: #f5f5f5;
@border-color: #d9d9d9;
// 间距
@spacing-xs: 4px;
@spacing-sm: 8px;
@spacing-md: 16px;
@spacing-lg: 24px;
@spacing-xl: 32px;
// 圆角
@radius-sm: 4px;
@radius-md: 8px;
@radius-lg: 16px;
// 阴影
@shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
@shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
@shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);4.3 组件直接使用(无需 import)
// components/Button/style.less
// ✅ 全局变量已自动注入,直接使用
.btn {
display: inline-flex;
align-items: center;
padding: @spacing-sm @spacing-md;
border-radius: @radius-md;
font-size: 14px;
border: 1px solid @border-color;
cursor: pointer;
transition: all 0.2s;
box-shadow: @shadow-sm;
&--primary {
background: @primary-color;
color: #fff;
border-color: @primary-color;
&:hover {
background: darken(@primary-color, 8%);
}
}
&--large {
padding: @spacing-md @spacing-lg;
font-size: 16px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}// components/Card/style.less
// ✅ 同样无需 import
.card {
background: @bg-color;
border: 1px solid @border-color;
border-radius: @radius-md;
padding: @spacing-lg;
box-shadow: @shadow-sm;
&__title {
font-size: 16px;
font-weight: 600;
color: @text-color;
margin-bottom: @spacing-md;
}
&__content {
color: @text-color-secondary;
line-height: 1.6;
}
}4.4 主题切换实现
通过 CSS 自定义属性(CSS Variables)实现运行时主题切换:
// styles/themes/light.less
// 浅色主题:将 Less 变量映射为 CSS 自定义属性
:root,
[data-theme='light'] {
--primary-color: @primary-color;
--text-color: @text-color;
--bg-color: @bg-color;
--border-color: @border-color;
--shadow-sm: @shadow-sm;
}// styles/themes/dark.less
// 深色主题:覆盖 CSS 自定义属性
[data-theme='dark'] {
--primary-color: #177ddc;
--text-color: rgba(255, 255, 255, 0.85);
--bg-color: #141414;
--border-color: #434343;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
}JS 端切换主题:
// theme.ts
function setTheme(theme: 'light' | 'dark') {
document.documentElement.setAttribute('data-theme', theme)
localStorage.setItem('theme', theme)
}4.5 Vite 配置中的注入
// vite.config.ts
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
css: {
preprocessorOptions: {
less: {
additionalData: `
@import "@/styles/variables.less";
@import "@/styles/mixins.less";
`,
javascriptEnabled: true,
// 开启 math 严格模式,避免 Less 3.x/4.x 兼容性问题
math: 'always',
},
},
},
})五、高级技巧
5.1 按环境注入不同变量
不同环境(开发/生产)可能需要不同的变量值:
// vite.config.ts
const isDev = process.env.NODE_ENV === 'development'
export default defineConfig({
css: {
preprocessorOptions: {
less: {
additionalData: `
@import "@/styles/variables.less";
${isDev ? `@import "@/styles/dev-overrides.less";` : ''}
`,
},
},
},
})5.2 排除第三方库
自动注入可能会影响 node_modules 中的第三方 Less 文件(如 Ant Design),需要排除:
// webpack.config.js
{
test: /\.less$/,
// ❌ 排除 node_modules,避免给第三方库注入全局变量
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: {
additionalData: `@import "@/styles/variables.less";`,
},
},
],
},
// ✅ 第三方库使用另一个规则,不注入
{
test: /\.less$/,
include: /node_modules/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: {
lessOptions: { javascriptEnabled: true },
},
},
],
},Vite 中通过 include / exclude 参数控制:
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
less: {
additionalData: (content, filePath) => {
// 不对 node_modules 中的文件注入
if (filePath.includes('node_modules')) {
return content
}
return `@import "@/styles/variables.less";\n${content}`
},
// ⬆️ additionalData 支持函数签名
},
},
},
})Vite 的 additionalData 支持函数签名:
additionalData: (content: string, filePath: string) => string这让你可以按文件路径有选择地注入。
5.3 使用 modifyVars 运行时覆盖
部分场景需要在运行时修改变量(如换肤):
// webpack.config.js
{
loader: 'less-loader',
options: {
lessOptions: {
modifyVars: {
'primary-color': '#722ed1', // 覆盖主色
'font-size-base': '16px',
},
javascriptEnabled: true,
},
additionalData: `@import "@/styles/variables.less";`,
},
}modifyVars 的优先级高于 additionalData 中 @import 的变量,适合做构建时的主题定制。
六、注意事项
6.1 变量重复定义问题
自动注入后,组件文件中不要重复 import 全局变量文件:
// ❌ 错误:重复导入导致编译警告或变量冲突
@import '../../styles/variables.less';
.my-class {
color: @primary-color;
}
// ✅ 正确:直接使用,无需 import
.my-class {
color: @primary-color;
}6.2 副作用问题
自动注入的文件中不要包含实际的 CSS 输出:
// ❌ 错误:全局变量文件中不要有 CSS 输出
@primary-color: #1890ff;
body { // ← 这会在每个组件文件中重复输出
font-family: sans-serif;
}
// ✅ 正确:只放变量定义和 mixin
@primary-color: #1890ff;
@font-family: sans-serif;否则每个编译后的 CSS 文件中都会重复输出这些样式,导致包体积膨胀。
6.3 Less 版本兼容性
| Less 版本 | additionalData | math 模式 | 注意 |
|---|---|---|---|
| 3.x | ✅ 支持 | 默认 parens-division |
除法用 () 包裹 |
| 4.x | ✅ 支持 | 需设置 math: 'always' |
不加 () 的除法也支持 |
// Less 4.x 推荐配置
css: {
preprocessorOptions: {
less: {
math: 'always',
additionalData: `@import "@/styles/variables.less";`,
},
},
},6.4 性能影响
自动注入会对每个 Less 文件增加一次 @import 解析,但影响通常可忽略。如果项目极大(数千个 Less 文件),可以:
- 只对
src/目录注入,排除node_modules - 合并全局变量为一个文件(减少
@import次数) - 使用 CSS 自定义属性替代部分 Less 变量
七、总结
| 配置方法 | Webpack | Vite | Vue CLI |
|---|---|---|---|
| 参数名 | prependData / additionalData |
additionalData |
additionalData |
| 支持函数 | ❌ | ✅(按文件路径选择注入) | ❌ |
| 排除 node_modules | 需拆分 rule | 函数签名判断 | 需 chainWebpack |
| 配置位置 | less-loader options |
css.preprocessorOptions.less |
chainWebpack |
核心要点:
- 自动注入让组件代码更干净,省去重复的
@import - Webpack 用
prependData/additionalData,Vite 统一用additionalData - 变量文件不要包含实际 CSS 输出,避免样式重复
- 排除
node_modules,避免干扰第三方库 - 与 CSS 自定义属性配合,可以实现运行时主题切换
additionalData支持函数签名(Vite),可以实现按需注入
自动注入 Less 全局变量是小投入大回报的工程化改造——一次配置,永远省去重复 import,让代码更聚焦于业务本身。