【问题标题】:removing an imported CSS from react and replace it with new CSS从 react 中删除导入的 CSS 并将其替换为新的 CSS
【发布时间】:2021-09-06 09:04:28
【问题描述】:

我有一个巨大的 react 应用程序,它有 4 个主要的大型 CSS(darkLTR、lightLTR、darkRTL、lightRTL),我知道这并不理想,但这是我老板为我买的网站模板并让我使用它(我想使用 Material ui,它非常适合主题处理,但遗憾的是我不被允许)。它带有两个不同的模板。暗模式和亮模式。他们告诉我,他们希望同时拥有深色和浅色模式,甚至 RTL 支持。所以我不得不将 ltr 翻译成 rtl 并添加切换按钮以在不同的主题和语言之间切换。我做了所有这些,现在我有所有样式的 4 个 CSS 文件。它们都有相同的类名和规则。唯一不同的是方向和颜色。我像下面这样导入这些 CSS 文件:

export default function importStylesheets() {
    const theme = localStorage.getItem('theme')
    const direction = localStorage.getItem('direction')
    if (theme === 'dark' && direction === 'rtl') {
        import('./css/styleDarkrtl.css')
    } else if (theme === 'dark' && direction === 'ltr') {
        import('./css/styleDarkltr.css')
    } else if (theme === 'light' && direction === 'rtl') {
        import('./css/styleLightrtl.css')
    } else if (theme === 'light' && direction === 'ltr') {
        import('./css/styleLightltr.css')
    }
}

现在我的问题开始了。想象一下,我们处于 darkltr 模式,并且 darkltr CSS 已经加载。现在用户切换主题切换按钮。所以importStylesheets()函数会被再次调用,这一次主题的值会变轻。所以这次 lightltr 将被导入,整个主题将得到更新,一切看起来都很酷。但是如果用户再次切换主题切换按钮怎么办?什么都没发生。因为现在我调用importStylesheets() 函数,它不会重新导入darkltr CSS,因为它已经在应用程序缓存中。最后一个导入的文件(在本例中为 lightltr)具有更高的优先级。所以什么都不会改变。我想出了一个临时解决方案来重新加载整个应用程序并使用 localStorage 加载它,然后调用 importStylesheets() 函数。但这是一个巨大的劣势,因为我们使用 React 应用程序的原因之一是为了获得良好且快速的用户体验。所以我的问题是,有人可以帮我清除导入的内容吗?管他呢。我只需要能够随时在不同的 CSS 之间切换或将它们放在首位。请帮助我实现这个目标。非常感谢。

更新: 我在捆绑文件中找到了这部分代码。认为它可能会有所帮助

function importStylesheets() {
  const theme = localStorage.getItem('theme');
  const direction = localStorage.getItem('direction');

  if (theme === 'dark' && direction === 'rtl') {
    Promise.all(/*! import() */[__webpack_require__.e(0), __webpack_require__.e(1)]).then(__webpack_require__.t.bind(null, /*! ./css/styleDarkrtl.css */ "./src/css/styleDarkrtl.css", 7));
  } else if (theme === 'dark' && direction === 'ltr') {
    Promise.all(/*! import() */[__webpack_require__.e(0), __webpack_require__.e(3)]).then(__webpack_require__.t.bind(null, /*! ./css/styleDarkltr.css */ "./src/css/styleDarkltr.css", 7));
  } else if (theme === 'light' && direction === 'rtl') {
    Promise.all(/*! import() */[__webpack_require__.e(0), __webpack_require__.e(2)]).then(__webpack_require__.t.bind(null, /*! ./css/styleLightrtl.css */ "./src/css/styleLightrtl.css", 7));
  } else if (theme === 'light' && direction === 'ltr') {
    Promise.all(/*! import() */[__webpack_require__.e(0), __webpack_require__.e(4)]).then(__webpack_require__.t.bind(null, /*! ./css/styleLightltr.css */ "./src/css/styleLightltr.css", 7));
  }
}

【问题讨论】:

  • 你在使用 Create React App 吗?
  • 是的,我正在使用 Create React App

标签: javascript css reactjs


【解决方案1】:

对于仍然想知道如何轻松实现此目标的每个人,您有两个选择:
1-您可以使用纯js来实现这一点。首先将所有的 CSS 放在应用程序的公共文件夹中。然后你可以这样做:

const head = document.querySelector("head")
let link = document.getElementById("css-link")
if (!link) { // for first render and ensuring that the link does not already exist
    link = document.createElement('link')
    link.rel = "stylesheet"
    link.href = `/css/style${theme === 'dark' ? 'Dark' : 'Light'}${direction}.css`
    link.id = "css-link"
} else {
    link.href = `/css/style${theme === 'dark' ? 'Dark' : 'Light'}${direction}.css`
}

2- 借助react-helmet 包,您无需使用纯js,也无需痛苦。您需要做的就是将所有 css 放在公共文件夹中,然后执行以下操作:

import { Helmet } from 'react-helmet'
.
.
.
<Helmet >
        <link rel="stylesheet" href={`/css/style${theme === 'dark' ? 'Dark' : 'Light'}${direction}.css`} />
 </Helmet>
.
.
.

使用我上面提到的两种方式,我可以轻松地在不同的 CSS 之间切换。
重要提示
您必须在 href 属性中使用 "/" 开始寻址,而不是使用 "./"(如果您的网站在根目录下提供)

【讨论】:

  • 我同意这是一个相对容易的选择。但是,我必须指出,这会带来一系列主要缺点,如official docs 中所述。充其量,这会破坏缓存失效、散列、代码压缩、代码拆分/分组和许多其他功能,因为这是一种 Webpack 解决方法。总体而言,它会对性能产生负面影响。
  • 感谢您告诉我。每当我有时间测试它时,我都会尝试一下:)
【解决方案2】:

好吧,问题是至少有两种不同的机制促成了当前的行为(Webpack 模块引擎和 Webpack 的 style-loader)。

当您使用 Create React App 时,您的代码使用负责模块创建、加载和执行的 Webpack 进行捆绑。

Webpack 模拟 NodeJS 模块缓存策略,所以你是对的,每个模块只执行一次(你可以 delete the corresponding entry 来自 require.cache 使用模块 ID 来强制执行一个模块多次,但在你的情况下它会每次您决定加载css 文件时,都会用新的&lt;style&gt; 标签污染您的&lt;head&gt;。旧的不会被删除。

事实上,style-loader 将您的 css 代码转换为伪 JavaScript 模块。执行时,将根据指定的选项应用样式。默认情况下会创建&lt;style&gt; 标签。

所以,这就是为什么您的代码表现得如您所见。当调用import()时,Webpack模块引擎(Webpack Runtime)会搜索对应的模块(也就是你更新中的__webpack_require__东西)并执行。然后,在内部,style-loader 设置的包装器使用&lt;style&gt; 标签插入您的样式。最后,Webpack 将模块添加到缓存中,下次不会再次执行,而是返回最新的export(在这种情况下,它是一个包含所有类名的对象)。

碰巧style-loader 有一个我认为与您的用例完全匹配的功能,它被称为lazyStyleTag(请参阅here)。但是您必须更改 Webpack 的配置,这并不那么简单,特别是在使用 Create React App 时。

如果您需要帮助更改 Webpack 的配置,请在 cmets 中告诉我,我会尽力指导您完成整个过程。

但是,最后,您应该能够执行以下操作:

let previousStylesheet = null;

export async function importStylesheets() {
    const theme = localStorage.getItem('theme');
    const direction = localStorage.getItem('direction');

    let newStylesheet = null;

    if (theme === 'dark' && direction === 'rtl') {
        newStylesheet = await import('./css/styleDarkrtl.css');
    } else if (theme === 'dark' && direction === 'ltr') {
        newStylesheet = await import('./css/styleDarkltr.css');
    } else if (theme === 'light' && direction === 'rtl') {
        newStylesheet = await import('./css/styleLightrtl.css');
    } else if (theme === 'light' && direction === 'ltr') {
        newStylesheet = await import('./css/styleLightltr.css');
    }

    if (previousStylesheet) {
      previousStylesheet.unuse();
    }

    if (newStylesheet) {
      newStylesheet.use();
    }

    previousStylesheet = newStylesheet;
}

你的类名可以这样访问:

import styles from './css/styleDarkrtl.css';

const elem = <div className={styles.locals['my-class']} />;

【讨论】:

  • 感谢您的回复和研究。但是感谢@KarelG,我发现了一个名为react-helmet 的东西。这个包将操作 html 的 head 标签。老实说,我正在做类似body.appendChild(link) 的事情,但问题是,我必须将我的样式表放在 index.html 附近的公共文件夹中,这样反应头盔就可以很好地工作
  • @ArdalanNahavandi 很高兴您正在接近解决方案。请查看我对其他答案的评论以了解一些重要的观察结果。
  • 正如您在我的另一个答案中提到的那样,公用文件夹非常慢。每当用户尝试切换主题时,样式更改需要大约半秒到一秒的时间。并且在那段时间里,页面没有样式!这是很棒的用户体验。我试过你的答案,它运行良好,没有任何延迟。感谢您的建议
  • @ArdalanNahavandi 我真的很高兴读到这个。只是出于好奇,您是如何更改 Webpack 配置的?重新布线?弹出?
  • 大约 3 天前我退出了我的 react 应用程序。因为我需要一个 html 加载器,但我无法通过重新布线来处理它。由于我的应用程序已经被弹出,我只是在我的 webpack 配置中添加了另一个规则。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 2018-07-03
  • 2021-05-23
  • 2020-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多