将 NProgress 与“React.lazy”一起使用

Posted

技术标签:

【中文标题】将 NProgress 与“React.lazy”一起使用【英文标题】:Use NProgress with "React.lazy" 【发布时间】:2019-10-31 14:43:13 【问题描述】:

我有以下组件树:

<BrowserRouter>
  <Suspense fallback=<h1>MyFallback</h1>>
    <Switch>
      <Route component=HomePage path="/" exact />
      <Route
        component=lazy(() => import('./pages/Auth/Login'))
        path="/auth/login"
        exact
      />
    </Switch>
  </Suspense>
</BrowserRouter>

我使用React.Suspense 来显示加载回退。但是,现在我想在当前页面的顶部显示一个进度条,而不是使用普通的 Suspense 加载回退,这会删除整个当前路由以显示回退。

如何添加 NProgress,例如,指示正在加载的页面的加载进度?

也许新的 React 的并发模式可以帮助解决这个问题? :)

【问题讨论】:

我正在尝试做同样的事情你有没有找到这个问题的答案 不,我没有。 :( @LuizFelipe 现在呢?我也在寻找同样的东西。 @KutsanKaplan,我把这个想法抛在脑后。但也许当新的并发模式发布时,我们也许可以做这样的事情。 【参考方案1】:

解决办法

const LazyLoad = () => 
    useEffect(() => 
        NProgress.start();

        return () => 
            NProgress.stop();
        ;
    );

    return '';
;

<Suspense fallback=<LazyLoad />>

【讨论】:

【参考方案2】:
import  useEffect  from "react";
import NProgress from "nprogress";
import "nprogress/nprogress.css";

export default function TopProgressBar() 
  useEffect(() => 
    NProgress.configure( showSpinner: false );
    NProgress.start();

    return () => 
      NProgress.done();
    ;
  );

  return "";

【讨论】:

【参考方案3】:

以下内容未经测试,因为我已将其从更高级的配置中提取出来,但它应该可以工作。如果您有困难,请发布,以便我们更新并解决问题。

npm install react-use react-helmet-async nprogress

创建名为“useMounted”的钩子

import useEffect, useRef from 'react';
import useUpdate from 'react-use';

export default function useMounted() 
  const mounted = useRef(false);
  const update = useUpdate();
  useEffect(() => 
    if (mounted.current === false) 
      mounted.current = true;
      update();
    
  , [update]);
  return mounted.current;

创建“ProgressBar”组件

这将允许您传递道具来自定义您的进度条。请注意,这是一个有限的示例,请参阅 NProgress css 文件了解您可能希望修改的其他 css 样式。

import Helmet from 'react-helmet-async';
import useMounted from '../hooks/useMounted'; // your path may differ.
import  useLocation  from 'react-router-dom'; // not needed for nextjs
import nprogress from 'nprogress';

const ProgressBar = (props?) => 
  
  props = 
    color: 'red',
    height: '2px',
    spinner: '20px',
    ...props
  ;

  // if using NextJS you will not need the below "useMounted" hook
  // nor will you need the below "useEffect" both will be 
  // handled by the Router events in the below Bonus
  // monkey patch.
  
  const mounted = useMounted();
  const  pathname  = useLocation(); // assumes react router v6
  const [visible, setVisible] = useState(false);

  useEffect(() => 
    if (!visible) 
      nprogress.start();
      setVisible(true);
    
    if (visible) 
      nprogress.done();
      setVisible(false);
    
    if (!visible && mounted) 
      setVisible(false);
      nprogress.done();
    
    return () => 
      nprogress.done();
    
    // eslint-disable-next-line react-hooks/exhaustive-deps
  , [pathname, mounted]);

  // if using the below styles with NextJS wrap the below in
  //     <style jsx global>`styles here `</style>;
  // you will not need Helmet either simply return the
  // jsx style.

  const styles = `
     #nprogress .bar 
        background: $props.color;
        height: $props.height;
     
     #nprogress .peg 
        box-shadow: 0 0 10px $props.color, 0 0 5px $props.color;
     
     #nprogress .spinner-icon 
        width: $props.spinner;
        height: $props.spinner;
        border-top-color: $props.color;
        border-left-color: $props.color;
     
  `;

  return (
    <Helmet>
      <style>styles</style>
    </Helmet>
  );
;
export default ProgressBar;

使用您的进度条组件

此处显示默认create-react-app 应用程序。

注意:此示例基于 react-router 版本 6

import React from 'react';
import ReactDOM from 'react-dom';
import ProgressBar from './components/ProgressBar'; // your path may differ

import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';
import  BrowserRouter, Routes  from 'react-router-dom';

import './index.css';
import 'nprogress/nprogress.css';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <ProgressBar />
      <Routes>
       /* your routes here */
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

奖金!猴子补丁抓取触发抓取进度条。

import nprogress from 'nprogress';
// import Router from 'next/router'; // uncomment for NextJS

function DOMEnabled() 
  return !!(
    typeof window !== 'undefined' &&
    window.document &&
    window.document.createElement
  );


// let timer: NodeJS.Timeout; // for typescript use.
let timer;
let state: string;
let activeRequests = 0;

const delay = 250;

function load() 
  if (state === 'loading') return;
  state = 'loading';
  timer = setTimeout(function () 
    nprogress.start();
  , delay); // only show if longer than the delay


function stop() 
  if (activeRequests > 0) return;
  state = 'stop';
  clearTimeout(timer);
  nprogress.done();


// Uncomment if using [NextJS][2]

// Router.events.on('routeChangeStart', load);
// Router.events.on('routeChangeComplete', stop);
// Router.events.on('routeChangeError', stop);

if (DOMEnabled()) 
  const _fetch = window.fetch;
  window.fetch = async function (...args) 
    if (activeRequests === 0) load();
    activeRequests++;
    try 
      const result = await _fetch(...args);
      return result;
     catch (ex) 
      return Promise.reject(ex);
     finally 
      activeRequests -= 1;
      if (activeRequests === 0) stop();
    
  ;


【讨论】:

【参考方案4】:

这是我使用反应钩子的解决方案。

import React,  useEffect  from 'react';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

const Loading = () => 
  useEffect(() => 
    NProgress.start();
    return () => 
      NProgress.done();
    ;
  , []);
  return (
    <Row>
      <Col span=12 offset=6>
        Loading
      </Col>
    </Row>
  );
;

export default Loading;

如您所见,我使用useEffect 来检测组件状态。

    NProgress.start(); 在组件挂载时调用 NProgress.done(); 在组件卸载时作为清理调用。

返回值是可选的,你可以渲染任何你想要的。

您还可以使用基于类的组件来实现相同的结果。为此,您可以使用componentWillUnmount()componentDidMount()

【讨论】:

以上是关于将 NProgress 与“React.lazy”一起使用的主要内容,如果未能解决你的问题,请参考以下文章

使用 React.lazy 与 webpack 动态导入?

React.lazy与Suspence实现延迟加载

react s-s-r 与代码拆分而不是 React.Lazy 的优缺点

React.lazy 会提高 React Native 的性能吗?

用React.lazy和Suspense优化React代码打包

如何使用React.lazy和Suspense进行组件延迟加载