Webpack + postcss + 主题 + 热加载

Posted

技术标签:

【中文标题】Webpack + postcss + 主题 + 热加载【英文标题】:Webpack + postcss + themes + hot reloading 【发布时间】:2016-12-22 03:25:21 【问题描述】:

我想允许用户在我的网站上选择主题,他们将通过下拉菜单来选择。不过,我目前在不编写愚蠢代码的情况下无法弄清楚如何支持此功能。

我目前使用 css-modules 和 postcss 来让我的生活更轻松。 Webpack 用于将所有内容捆绑在一起。我在开发过程中使用热重载,在此期间我使用样式加载器。

完整配置如下所示


  test: /\.css$/,
  loader: !isDev
    ? ExtractTextPlugin.extract('style-loader', 'css-loader?sourcemap&minimize&modules&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]!postcss-loader')
    : 'style-loader!css-loader?sourcemap&minimize&modules&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]!postcss-loader'
,

解决方案 1

在 body 标记上放置一个类名以确定加载的主题。然后用于从每个组件 css 中选择正确的主题。这很乏味,我想避免,但它看起来像这样:

.myComponent 
  margin: 10px;
  width: 100px;


:global(.theme1) 
  .myComponent 
    background: $theme1_myComponent_background;
  


:global(.theme2) 
  .myComponent 
    background: $theme2_myComponent_background;
  

这很快就会变成难以扩展和不可维护的事情,这就是我想避免它的原因。

解决方案 2

为 css 生成预先主题的静态包。当用户请求/main.css 时,服务器上的处理程序确定要发送哪个主题(可能是查询字符串)并发送它们theme1-main.csstheme2-main.css 或类似的东西。问题是我不知道如何支持开发环境。

这样做的好处是我的 css 文件不需要任何额外且难以维护的逻辑:

.myComponent 
  margin: 10px;
  width: 100px;
  background: $myComponent_background;

解决方案 3

使用原生 css 变量。然后可以在运行时更新它们,这将反映已经加载的 css 并在生产和开发环境中正常工作。我的组件也看起来像(或类似于)解决方案 2。这里的问题是它们本身并没有得到广泛的支持,我不确定是否有任何值得研究的 polyfill 可以为我做这种事情。


总而言之,这些是我的想法。我还没有得出任何明确的结论,并希望得到所有关于如何解决这个问题的反馈。也许我在想这一切都错了,有一种更好的方法可以做我想做的事。

提前致谢!

【问题讨论】:

我认为解决方案 2 更好,前提是您可以自动完成(为单个主题构建网站,并且自动制作其他捆绑包)。 @AshishChaudhary 当然可以,但是我如何启用它进行开发?我在开发过程中不请求 CSS 包。 您最终找到解决方案了吗?也遇到了这个确切的问题。 @Dean 不。我暂时选择了解决方案 1,直到它在我的脸上爆炸。最好的解决方案是在原生 CSS 变量可用时使用它们。 【参考方案1】:

这是一个使用 SASS 的解决方案。它基本上是您的解决方案 1,但更易于编写和维护。看起来你没有使用 SASS,但由于它是我能找到的供我个人使用的最佳解决方案,我认为值得分享......

这是 SCSS 代码 (CodePen here):

// Define theme variables

$themes: (
  default: ( // default theme
    bg-color: white,
    text-color: black,
  ),
  dark: (
    bg-color: black,
    text-color: white,
  ),
  colorful: (
    bg-color: green,
    text-color: orange,
  )
);

// This is how you use your themes

.example
  padding: 10px;
  @include themify 
    background: theme-get(bg-color);
    color: theme-get(text-color);
  

它需要这样才能工作:

@mixin themify() 
  // Iterate over the themes
  @each $theme-name, $theme in $themes 
    $current-theme: $theme !global;
    @if $theme-name == 'default' 
      @content;
     @else 
      .theme-#$theme-name & 
        @content;
      
    
  


@function theme-get($key, $theme: $current-theme) 
  $ret: map-get($theme, $key);
  @if not $ret 
    @error 'Your theme doesn\'t have a value for `#$key`.';    
  
  @return $ret;

生成的 CSS:

.example 
  padding: 10px;
  background: white;
  color: black;

.theme-dark .example 
  background: black;
  color: white;

.theme-colorful .example 
  background: green;
  color: orange;

这在很大程度上受到了以下启发:

https://www.sitepoint.com/sass-theming-neverending-story/ https://jaicab.com/sass-vary/

【讨论】:

【参考方案2】:

一种可能的方法如下

    不要使用style-loader,因为它会自动插入 css,而是手动执行何时我们需要和我们需要什么

例如:

const css1 = require("raw!./App1.css");
const css2 = require("raw!./App2.css");

(这里需要raw-loader

    然后我们只需将所需的样式插入到文档中。

例如:

const style = document.createElement("link");
style.type = "stylesheet/text-css";
style.rel = "stylesheet";
style.href=`data:text/css,$css1`;
// style.href=`data:text/css,$css2`;
style.id = "theming";

document.querySelector("head").appendChild(style);
    然后您可以根据需要更改主题

href换个风格就行了:

style.href=`data:text/css,$css2`

或者让我们通过 React 做同样的事情:

import React,  Component  from 'react';
import logo from './logo.svg';

class App extends Component 
  constructor(props) 
    super(props);

    this.cssThemes = [];
    this.cssThemes.push(require("./App.css"));
    this.cssThemes.push(require("./App1.css"));
    this.cssThemes.push(require("./App2.css"));
    this.cssThemes.push(require("./App3.css"));

    this.state =  
      themeInd: 0,
    
  
  render() 
    return (
      <div >
      <style>
        this.cssThemes[this.state.themeInd]
      </style>
        <div className="App-header">
          <img src=logo className="App-logo"  />
          <h2>Welcome to React</h2>
        </div>
        <p >
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        <button 
          onClick=() => this.setState(themeInd: this.state.themeInd+1)
        >
          Change theme
        </button>
      </div>
    );
  


export default App;

如果你在 webpack.config 中设置 raw-loader 来处理 css 文件就可以了


  test: /\.css$/,
  loader: 'raw'
,

【讨论】:

以上是关于Webpack + postcss + 主题 + 热加载的主要内容,如果未能解决你的问题,请参考以下文章

【Webpack4】CSS 配置之 postcss-loader

无法让 PostCSS 与 Webpack 一起使用

Webpack postcss loader,它的目的是啥?

postcss 不能与 webpack 5 和 sass 结合使用

使用 webpack 和 postcss-loader 导入字体很棒

带有 less 和 postcss 自动前缀的 webpack