Webpack + postcss +主题+热重新加载

时间:2016-08-15 21:23:06

标签: css reactjs themes webpack postcss

我想允许用户在我的网站上选择主题,他们将通过下拉菜单来完成。我现在无法弄清楚如何支持这个功能,但没有编写愚蠢的代码。

我目前使用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标记上放置一个classname以确定加载了哪个主题。然后,这用于从每个组件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 个答案:

答案 0 :(得分: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;
}

受到以下方面的启发:

答案 1 :(得分:0)

一种可能的方法可以如下

  1. 不使用style-loader,因为它会自动插入css,但我们需要 我们需要的
  2. 例如:

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

    (我们需要raw-loader

    1. 然后我们只需要将所需的样式插入文档。
    2. 例如:

      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);
      
      1. 然后您可以在需要时更改主题
      2. 将另一种风格添加到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" alt="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;
        

        如果你设置raw-loader来处理webpack.config中的css文件,它将会起作用

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