【问题标题】:conditional css in create-react-appcreate-react-app 中的条件 CSS
【发布时间】:2018-03-31 20:11:55
【问题描述】:

我有默认的 css 文件和单独的 css 文件,只有在满足某些条件时才应该应用(以 owerride 默认值)。

我正在使用默认 import 'file.css' 语法的 create-react-app。

决定是否动态加载特定 css 文件的最佳方法是什么?

【问题讨论】:

  • 您可以改用require('file.css')import('file.css') 语法——这将允许您在条件句中使用它。
  • import('file.css') 不起作用,而 require 则将其添加为答案,以便我接受。谢谢
  • 对于import() 语法,您可能需要babel-plugin-syntax-dynamic-import。 Webpack 2 已经开箱即用地支持它,但是 Babel 需要能够解析它。但是请注意,import() 是异步的,require 不是。

标签: css reactjs create-react-app


【解决方案1】:

require 方法仅在开发中有效(因为所有 CSS 在构建时捆绑),import 方法根本不起作用(使用 CRA 版本 3.3)。

在我们的例子中,我们有多个主题,无法捆绑 - 所以我们使用 React.lazyReact.Suspense 解决了这个问题。

我们有ThemeSelector,它有条件地加载正确的css。

import React from 'react';

/**
 * The theme components only imports it's theme CSS-file. These components are lazy
 * loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
 */
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));

const ThemeSelector: React.FC = ({ children }) => (
  <>
    {/* Conditionally render theme, based on the current client context */}
    <React.Suspense fallback={() => null}>
      {shouldRenderTheme1 && <Theme1 />}
      {shouldRenderTheme2 && <Theme2 />}
    </React.Suspense>
    {/* Render children immediately! */}
    {children}
  </>
);

export default ThemeSelector;

Theme 组件唯一的工作就是导入正确的 css 文件:

import * as React from 'react';

// ? Only important line - as this component should be lazy-loaded,
//    to enable code - splitting for this CSS.
import 'theme1.css';

const Theme1: React.FC = () => <></>;

export default Theme1;

ThemeSelector 应将App 组件包装在src/index.tsx 中:

import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';

ReactDOM.render(
  <ThemeSelector>
    <App />
  </ThemeSelector>,
  document.getElementById('root')
);

据我了解,这会强制将每个 Theme 拆分为单独的包(实际上也拆分 CSS)。


如 cmets 中所述,此解决方案没有提供切换主题运行时的简单方法。此解决方案侧重于将主题拆分为单独的包。

如果您已经将主题拆分为单独的 CSS 文件,并且想要交换主题运行时,您可能需要查看使用 ReactHelmet (illustrated by @Alexander Ladonin's answer below) 的解决方案

【讨论】:

  • 我们必须将一些 JSX 传递给 fallback 属性(至少对于 React 16.12),而不是函数。 null&lt;div /&gt; 之类的东西(当然,在这种情况下更有用的东西。很好的例子!
  • 一旦加载,似乎没有办法卸载打包的 CSS,是吗?换句话说,如果不重新加载整个页面,我就无法创建在浅色/深色主题之间切换的开关。
  • 我不太确定这是一个解决方案。它仅适用于初始负载。使用此方法进行初始渲染后,无法更改主题。这是因为一旦加载了“Suspense”,它就不能被修改,除非使用上下文挂钩(结果不稳定,相信我我试过了)。另一种选择是在更改主题时强制重新加载
  • 这绝对是我找到的最好的解决方案,谢谢先生
【解决方案2】:

您可以改用require('file.css') 语法。这将允许您将其放在条件中。

例如

if(someCondition) {
    require('file.css');
}

【讨论】:

  • 嗯。看起来它只在开发模式下工作 - 在部署到服务器之后,他的服务器似乎无论如何都加载了 css,即使特定的代码块从未执行过。毕竟css组装可能发生在页面加载之前:/
  • @MartinsUntals 是的...如果您使用的是 ExtractTextPlugin,就会发生这种情况。您可以使用 CSS 模块并定位您的特定组件,这样 CSS 是否在每个页面上都无关紧要,或者您可以 take a look at this - 我还没有尝试过,但它可能更接近你的想要。
  • @MartinsUntals 你能在部署/生产级别找到动态 CSS 的解决方案吗?
  • 我确实使用了 react html 注入策略。从长远来看还不够好
  • 谢谢!当我觉得必须存在一个简单的解决方案时,我一直在避免与 webpack 搏斗!由于过度处理这个问题,我的大脑现在又冷静了。
【解决方案3】:

使用React Helmet。它动态地将链接、元标记等添加到文档标题中。 将其添加到任何渲染方法中。

import {Component} from 'react';
import ReactHelmet from 'react-helmet';

class Example extends Component{
    render(
        <ReactHelmet link={
            [{"rel": "stylesheet", type:"text/css", "href": "/style.css"}]
        }/>);
    }
}

您可以在下一次&lt;ReactHelmet/&gt; 渲染时重写它。

【讨论】:

    【解决方案4】:

    我发现在生产中有效的一个简单解决方案是使用 vercel 的 styled-jsx。首先,安装styled-jsx:

    npm install --save styled-jsx
    

    或者如果你使用 Yarn:

    yarn add styled-jsx
    

    现在从您的 css 文件创建字符串,例如:

    const style1 = `
    div {
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    `
    const style2 = `
    div {
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    `
    

    然后在您的 React 组件中,您可以执行以下操作:

    const MyComponent = () => {
      return (
        <div className='my-component'>
          <style jsx>
            {
              conditionA ? style1: style2
            }
          </style>
        </div>
      )
    }
    

    只需将&lt;style jsx&gt;{your_css_string}&lt;/style&gt; 添加到您希望添加样式的组件中,然后您就可以实现条件,只需使用不同的字符串来导入不同的css 样式。

    【讨论】:

      【解决方案5】:

      其他解决方案对我不起作用。经过一天的搜索,我得到了以下解决方案。在我的问题中,我有两个用于 RTL 或 LTR 的 CSS 文件,例如 app.rtl.cssapp.ltr.css

      像这样创建一个功能组件Style

      import React, { useState } from "react";
      
      export default function Style(props) {
        const [stylePath, setStylePath] = useState(props.path);
      
        return (
          <div>
            <link rel="stylesheet" type="text/css" href={stylePath} />
          </div>
        );
      }
      

      然后你就可以调用它了,比如App.js:

      function App() {
      ...
      return (    
        <Style path={`/css/app.${direction}.css`} />
      )}
      

      direction 参数包含rtlltr 并确定应加载哪个文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-23
        • 2020-07-23
        • 2017-06-24
        • 2021-10-11
        • 2018-12-18
        • 2020-12-02
        • 2018-10-11
        • 2019-10-29
        相关资源
        最近更新 更多