Webpack + postcss + 主题 + 热重载

IT技术 css reactjs themes webpack postcss
2021-04-29 21:07:11

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

我目前使用 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'
},

方案一

在 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。这里的问题是它们在本地没有得到广泛支持,我不确定是否有任何值得研究的 polyfills 可以为我做这种类型的事情。


总而言之,这些都是我的想法。我还没有得出任何明确的结论,并感谢所有关于我如何解决这个问题的反馈。也许我认为这一切都是错误的,并且有更好的方法来做我想做的事情。

提前致谢!

2个回答

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

这是 SCSS 代码(此处为CodePen):

// 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. 不要使用style-loader,因为它插入CSS自动,但做手工的时候,我们需要和什么我们所需要的。

例如:

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

(我们raw-loader这里需要

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

例如:

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. 然后您可以在需要时更改主题

只需将另一种样式设置为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 中设置to handle css 文件,它将起作用

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