解决根据环境变量实现的构建换肤未生效问题
在前台前端项目中实现了换肤功能,根据不同的环境显示不同的皮肤颜色。例如,在开思电商环境下,元素以红色背景呈现;而在博世环境下,元素以蓝色背景呈现。然而,当访问博世环境时,发现有一些元素没有正确显示,如下图所示:

分析
换肤的实现原理是通过sass-loader中的prependData属性,在构建过程中将自定义的样式变量添加到每个scss样式文件的头部。这样做可以覆盖默认设置的样式变量,从而实现换肤的效果。
以下是根据实际项目代码简化的示例代码:
// config-overrides.js
function rewireSassLoader(config, tenantId) {
// 查找对应的 rule
const rules = findRules(config, 'sass-loader');
rules.forEach(rule => {
rule.options.prependData = function(loaderContext) {
const tenantId = process.env.tenantId || TenantType.CASS;
const isBosch = tenantId === TenantType.BOSCH;
if (isBosch) {
const scssPath = path.resolve(__dirname, `src/styles/theme/${tenantId.toLowerCase()}.scss`);
const variables = require('fs').readFileSync(scssPath, 'utf-8');
return variables;
}
return '';
}
});
}
module.exports = function override(config, env) {
// 根据环境注入样式变量
rewireSassLoader(config);
return config;
};
源代码中追加样式变量的方式:
// node_modules/sass-loader/dist/getSassOptions.js options.data = loaderOptions.prependData ? typeof loaderOptions.prependData === 'function' ? loaderOptions.prependData(loaderContext) + _os.default.EOL + content : loaderOptions.prependData + _os.default.EOL + content : content; // opt.outputStyle
配置的脚本命令:
// --run--
"scripts": {
"start": "cross-env PUBLIC_PATH=./ react-app-rewired start NODE_ENV=development",
"start-bosch": "cross-env tenantId=BOSCH PUBLIC_PATH=./ react-app-rewired start NODE_ENV=development",
}
在执行yarn start-bosch命令时没有达到换肤效果,其中没有显示正确的元素基本都是@casstime/bricks组件库中的元素,所以我们来分析下追加的样式变量为何在@casstime/bricks组件没有生效。
拿到问题时,我首先怀疑的是追加样式变量在文件头部,会不会被已存在的样式变量覆盖掉,例如:
$color: #f2f2f2; // 追加的样式变量
$color: #2b2b2b; // 已存在的样式变量
.text {
color: $color;
}
// 编译后
.text {
color: #2b2b2b;
}
在实际项目中使用@casstime/bricks基础组件库时,并不会单独引入每一个组件的样式文件,而是在入口文件处全局引入组件库样式。
// app.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { renderRoutes, RouteConfig } from 'react-router-config';
import { HashRouter } from 'react-router-dom';
import routes from './config/routes';
import '@casstime/bricks/dist/bricks.production.css';
const root = createRoot(document.getElementById('root') as HTMLElement);
root.render(<HashRouter>{renderRoutes(routes as RouteConfig[])}</HashRouter>);
项目中全局引入的组件样式文件是普通的CSS文件而不是SCSS文件,那么这些CSS文件不会经过Sass Loader加载器的处理,也就不会往其中追加样式变量,使用的还是默认样式,所以@casstime/bricks组件元素样式显示不正确。
在入口文件处调整为引入组件库的SCSS样式文件:
import '@casstime/bricks/lib/styles/bricks.scss';
再次执行yarn start-bosch命令,可以看到元素都正确显示了:

为什么调整为全局引入组件库的SCSS样式文件后不会发生追加样式变量被已存在的样式变量覆盖呢?
仔细翻阅组件库样式文件书写,其结构与书写方式如下(简化):
// ├── styles
// ├── _button.scss
// ├── _variables.scss
// ├── _colors.scss
// ├── ...
// └── bricks.scss
// _colors.scss
$primary-color: #da2227 !default; // 主色、突出色
// _variables.scss
@import 'colors';
$border-width: 1px !default; // 默认边框宽度
// _button.scss
.br-button-primary {
backgroud-color: $primary-color;
}
// bricks.scss
@import 'variables';
@import 'button';
假如现在有根据环境变量往bricks.scss中追加样式变量$primary-color: #237fd6;,经过sass-loader处理后的结果如下:
// bricks.scss
$primary-color: #237fd6; // 追加的样式变量
$primary-color: #da2227 !default; // 已存在的样式变量
$border-width: 1px !default; // 默认边框宽度
.br-button-primary {
backgroud-color: $primary-color;
}
处理的结果中其中有一个$primary-color是带有!default标记的变量,如果在之前的代码中没有定义或赋值这个变量,它将被默认设置为#da2227。但是,之前代码已经定义了$primary-color: #237fd6;,那么默认值将会被忽略。所以,最后会采用追加的样式变量。
以上关于解决根据环境变量实现的构建换肤未生效问题的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 解决根据环境变量实现的构建换肤未生效问题
微信
支付宝