我试图在角度4项目中实现一种css主题。我们使用webpack 3进行捆绑。该产品旨在供多家公司使用,并且必须根据其品牌手册进行查看。所以我们需要主题。
我们将有几个版本,但我们不想拥有多个版本的代码。所有主题都应保留在相同的代码库中。差异很小:颜色,图标,字体 - 一切都可以在CSS中更改。
我想到了几种方法,最明显的是通过:host-context 为组件实现主题,并通过更改webpack的环境变量来更改主体类。有了这样的方法,我们将在我们的捆绑中找到每个主题,这是不好的。也许还有另一种方式?
我想知道是否有可能让webpack加载而不是要求的css文件。相反,它可以按模式查找另一个文件,如果存在,则使用该文件而不是原始文件。或者加载两个文件。
例如,我们有一个 button.component.ts ,用于导入 button.component.css 。如果我们不告诉webpack使用任何主题,它就像往常一样工作。但是如果我们这样做,它会尝试在同一目录中读取 button.component.theme-name.css 。如果该文件存在,则webpack将其替换(或一起)导入默认文件。
这基本上就是我想做的事情。我猜,相同的机制对角度的html模板很有用。
有插件可以做这样的魔术吗?或者是一些复杂的装载机选项?如果您有其他方法可以解决我的任务 - 请随时发表评论!
答案 0 :(得分:1)
我创建了一个加载器,它可以使用其兄弟的内容附加或替换加载文件的内容,该兄弟的名称中包含所选主题的标题。
TL; DR
- 使用加载程序创建文件。
- 在webpack config中使用它。
- 在THEME =< themeName>中运行webpack环境现状。
醇>
主题loader.js
const fs = require('fs');
const loaderUtils = require('loader-utils');
module.exports = function (mainData) {
const options = loaderUtils.getOptions(this);
let themeName = options.theme;
let mode = options.mode;
if (themeName) {
// default mode
if (!Object.keys(transform).includes(mode)) {
mode = 'replace';
}
// fileName.suffix.ext -> fileName.suffix.themeName.ext
const themeAssetPath = this.resourcePath.replace(/\.([^\.]*)$/, `.${themeName}.$1`);
const callback = this.async();
// for HMR to work
this.addDependency(themeAssetPath);
fs.readFile(themeAssetPath, 'utf8', (err, themeData) => {
if (!err) {
callback(null, transform[mode](mainData, themeData));
} else if (err.code === 'ENOENT') {
// don't worry! if it's not here then it's not needed
callback(null, mainData);
} else {
callback(err);
}
});
} else {
return mainData;
}
};
const transform = {
// concat theme file with main file
concat: (mainData, themeData) => mainData + '\n' + themeData,
// replace main file with theme file
replace: (mainData, themeData) => themeData
};
使用这个手工装载机的一个样本 webpack.config.js :
resolveLoader: {
modules: [
paths.libs, // ./node_modules
paths.config // this is where our custom loader sits
]
},
module: {
rules: [
// component styles
{
test: /\.css$/,
include: path.join(paths.src, 'app'),
use: [
'raw-loader',
// search for a themed one and append it to main file if found
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'concat'
}
}
]
},
// angular templates — search for a themed one and use it if found
{
test: /\.html$/,
use: ['raw-loader',
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'replace'
}
}
]
}
]
}
例如, app.component.css :
:host {
background: #f0f0f0;
color: #333333;
padding: 1rem 2rem;
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
}
nav {
/* ... */
/* something about nav element */
/* ... */
}
header {
/* ... */
/* pile of styles for header */
/* ... */
}
要实现黑暗主题,我们不需要更改所有弹性和填充工作人员,也可能导航和标题没有自己的背景和字体颜色设置。所以我们只需要覆盖主机元素样式。我们创建 app.component.dark.css :
:host {
background: #222222;
color: #e0e0e0;
}
我们运行webpack,环境变量THEME设置为黑暗。加载器接受处理 app.component.css 的请求,尝试加载 app.component.dark.css 并瞧!主题css附加到结果文件的末尾。因为级联,
如果多个竞争选择者具有相同的重要性和特异性,......后来的规则将胜过早期规则(MDN)。
对于HTML,我们没有这种方法。所以我们必须完全重写我们的模板。希望你不需要经常这样做。我的情况,我想改变页眉和页脚,以适应cutomer的品牌需求。
这是我第一次尝试创建webpack加载器,如果您发现问题,请发表评论。