如何解决React.js中的FOUC

时间:2018-06-05 07:09:56

标签: css reactjs webpack create-react-app fouc

我已经从create-react-app构建了react.js网站。 但是在生产模式中,存在FOUC,因为在呈现html之后加载样式。

有什么方法可以解决这个问题吗?我一直在谷歌搜索答案,但还没找到合适的答案。

1 个答案:

答案 0 :(得分:2)

FOUC

FOUC - 所谓的 Flash of Unstyled Content 可能与解决此问题的许多尝试一样非常棘手。

到了

让我们考虑以下路由配置(react-router):

...
<PageLayout>
  <Switch>
    <Route exact path='/' component={Home} />
    <Route exact path='/example' component={Example} />
  <Switch>
</PageLayout>
...

其中PageLayout是一个简单的hoc,包含带有page-layout类的div包装并返回它的子级。

现在,让我们关注基于路线的组件渲染。通常你会使用component prop作为反应Compoment。但在我们的情况下,我们需要动态地获取它,以应用有助于我们避免FOUC的功能。所以我们的代码看起来像这样:

import asyncRoute from './asyncRoute'

const Home = asyncRoute(() => import('./Home'))
const Example = asyncRoute(() => import('./Example'))

...

<PageLayout>
  <Switch>
    <Route exact path='/' component={Home} />
    <Route exact path='/example' component={Example} />
  <Switch>
</PageLayout>

...

澄清让我们也展示了asyncRoute.js模块的样子:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

import Loader from 'components/Loader'

class AsyncImport extends Component {
  static propTypes = {
    load: PropTypes.func.isRequired,
    children: PropTypes.node.isRequired
  }

  state = {
    component: null
  }

  toggleFoucClass () {
    const root = document.getElementById('react-app')
    if (root.hasClass('fouc')) {
      root.removeClass('fouc')
    } else {
      root.addClass('fouc')
    }
  }

  componentWillMount () {
    this.toggleFoucClass()
  }

  componentDidMount () {
    this.props.load()
      .then((component) => {
        setTimeout(() => this.toggleFoucClass(), 0)
        this.setState(() => ({
          component: component.default
        }))
      })
  }

  render () {
    return this.props.children(this.state.component)
  }
}

const asyncRoute = (importFunc) =>
  (props) => (
    <AsyncImport load={importFunc}>
      {(Component) => {
        return Component === null
          ? <Loader loading />
          : <Component {...props} />
      }}
    </AsyncImport>
  )

export default asyncRoute
  

hasClassaddClassremoveClass是对DOM类属性进行操作的polyfill。

     

Loader是一个显示微调器的自定义组件。

为什么setTimeout

仅仅因为我们需要在第二个刻度中移除fouc类。否则它将与渲染Component相同。所以它不会工作。

正如您在AsyncImport组件中看到的那样,我们通过添加fouc类来修改反应根容器。所以HTML是为了清晰起见:

<html lang="en">
<head></head>
<body>
  <div id="react-app"></div>
</body>
</html>

另一个难题:

#react-app.fouc
    .page-layout *
        visibility: hidden

在导入特定组件(即:HomeExample)时应用。

为什么不display: none

因为我们希望所有依赖于父宽度,高度或任何其他css规则的组件都能正确呈现。

它是如何工作的?

主要假设是隐藏所有元素,直到组件准备好向我们展示渲染内容。首先,它会触发asyncRoute函数,该函数会在Loader安装和渲染之前向我们显示Component。与此同时,在AsyncImport中,我们通过在反应根DOM元素上使用类fouc来切换内容的可见性。当一切都加载时,是时候展示一切,所以我们删除了那个类。

希望有所帮助!

感谢

article,我认为动态导入的想法来自react-loadable

来源

https://turkus.github.io/2018/06/06/fouc-react/