使webpack有条件地加载额外的或替代的css文件

时间:2017-09-15 07:44:15

标签: css angular webpack

我试图在角度4项目中实现一种css主题。我们使用webpack 3进行捆绑。该产品旨在供多家公司使用,并且必须根据其品牌手册进行查看。所以我们需要主题。

我们将有几个版本,但我们不想拥有多个版本的代码。所有主题都应保留在相同的代码库中。差异很小:颜色,图标,字体 - 一切都可以在CSS中更改。

我想到了几种方法,最明显的是通过:host-context 为组件实现主题,并通过更改webpack的环境变量来更改主体类。有了这样的方法,我们将在我们的捆绑中找到每个主题,这是不好的。也许还有另一种方式?

我想知道是否有可能让webpack加载而不是要求的css文件。相反,它可以按模式查找另一个文件,如果存在,则使用该文件而不是原始文件。或者加载两个文件。

例如,我们有一个 button.component.ts ,用于导入 button.component.css 。如果我们不告诉webpack使用任何主题,它就像往常一样工作。但是如果我们这样做,它会尝试在同一目录中读取 button.component.theme-name.css 。如果该文件存在,则webpack将其替换(或一起)导入默认文件。

这基本上就是我想做的事情。我猜,相同的机制对角度的html模板很有用。

有插件可以做这样的魔术吗?或者是一些复杂的装载机选项?如果您有其他方法可以解决我的任务 - 请随时发表评论!

1 个答案:

答案 0 :(得分:1)

我创建了一个加载器,它可以使用其兄弟的内容附加或替换加载文件的内容,该兄弟的名称中包含所选主题的标题。

  

TL; DR

     
      
  1. 使用加载程序创建文件。
  2.   
  3. 在webpack config中使用它。
  4.   
  5. 在THEME =< themeName>中运行webpack环境现状。
  6.   

主题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加载器,如果您发现问题,请发表评论。

相关问题