southliu / react-admin

React+Typescript+Vite+Antd+Zustand后台管理系统模版,支持keepalive功能、UnoCSS、动态菜单、国际化i18n、虚拟滚动表格。
https://southliu.github.io
MIT License
278 stars 48 forks source link

真正的路由加载进度条 #169

Closed condorheroblog closed 2 months ago

condorheroblog commented 2 months ago

顶部的滚动条目前项目实现代码如下,只会执行一次,并不会在每次切换路由显示:

  useEffect(() => {
    nprogress.done();

    // 首次进入清除版本缓存
    handleClearVersion();

    // 关闭loading
    const firstElement = document.getElementById('first');
    if (firstElement && firstElement.style?.display !== 'none') {
      firstElement.style.display = 'none';
    }

    return () => {
      nprogress.start();
    };
  }, []);

经过实践我认为可以通过 react-transition-group 实现,伪代码如下:

import { Suspense, useRef } from "react";
import { useLocation, useOutlet } from "react-router-dom";
import { SwitchTransition, Transition } from "react-transition-group";

import { NProgress } from "#src/utils";

export default function ParentLayout() {
    const currentOutlet = useOutlet();
    const location = useLocation();
    const nodeRef = useRef(null);

    return (
        <Suspense>
            <SwitchTransition>
                <Transition
                    nodeRef={nodeRef}
                    // 变相实现 react-router-dom 类似 vue-router 的 router.beforeEach 钩子
                    onEnter={() => { NProgress.start(); }}
                    // 变相实现 react-router-dom 类似 vue-router  的 router.afterEach 钩子
                    onEntered={() => { NProgress.done(); }}
                    key={location.pathname}
                    timeout={0}
                    unmountOnExit
                >
                    {_ => currentOutlet}
                </Transition>
            </SwitchTransition>
        </Suspense>
    );
}

我们是用 onEnter 和 onEntered 来监听路由的进入和进入完成,你觉得呢😂。

southliu commented 2 months ago

在src/router/App.tsx中添加:

import { useEffect } from "react";
import { useLocation, useRoutes } from "react-router-dom";
import nprogress from 'nprogress';

function App() {
  const location = useLocation();

  useEffect(() => {
    nprogress.done();

    return () => {
      nprogress.start();
    };
  }, [location]);

  // ...
}
southliu commented 2 months ago

发现刷新之后首次不显示进度条,在src/router/App.tsx中添加:

function App() {
  const location = useLocation();

  // 新增
  useEffect(() => {
    nprogress.start();
  }, []);

  useEffect(() => {
    nprogress.done();

    return () => {
      nprogress.start();
    };
  }, [location]);
condorheroblog commented 2 months ago

也是可以的👍

使用 react-transition-group 给页面切换增加路由动画,是否有考虑目前切换非常生硬。

southliu commented 2 months ago

可以的,有空我研究一下动画这块,感谢大佬分享😁。

condorheroblog commented 2 months ago

在src/router/App.tsx中添加:

import { useEffect } from "react";
import { useLocation, useRoutes } from "react-router-dom";
import nprogress from 'nprogress';

function App() {
  const location = useLocation();

  useEffect(() => {
    nprogress.done();

    return () => {
      nprogress.start();
    };
  }, [location]);

  // ...
}

我突然想到这个动画还是假的,它并没有关心子路由是否已经上树。

southliu commented 2 months ago

是指有三层路由关系,路由切换只变第三层组件的渲染的意思吗?

condorheroblog commented 2 months ago

当前的代码在每次 location 变化时都会调用 nprogress.done(),然后在组件卸载时再次调用 nprogress.start()。

我的意思是:无论子路由是否真正加载,进度条都会在路由变化时启动和结束,即使进度条结束了,组件可能还没上树。

southliu commented 2 months ago

我懂你意思了,确实有这个问题

condorheroblog commented 2 months ago

是的,母猪还没上树,进度条不能结束😂

southliu commented 2 months ago

学到了大佬,有空我去把那个动画加一下。

southliu commented 1 month ago

😭实现不出来,大佬有demo让我研究一下我哪里写错了吗

condorheroblog commented 1 month ago

我能想到的办法就是用的 react-transition-group 来实现的。

southliu commented 1 month ago

我用react-transition-group都没实现出来😂

condorheroblog commented 1 month ago

这是我的全部代码:

import { Suspense, useRef } from "react";
import { useLocation, useOutlet } from "react-router-dom";
import { SwitchTransition, Transition } from "react-transition-group";

import { NProgress } from "#src/utils";

export default function ParentLayout() {
    const currentOutlet = useOutlet();
    const location = useLocation();
    const nodeRef = useRef(null);

    return (
        <Suspense>
            <SwitchTransition>
                <Transition
                    nodeRef={nodeRef}
                    // 变相实现 react-router-dom 类似 vue-router 的 router.beforeEach 钩子
                    onEnter={() => { NProgress.start(); }}
                    // 变相实现 react-router-dom 类似 vue-router  的 router.afterEach 钩子
                    onEntered={() => { NProgress.done(); }}
                    key={location.pathname}
                    timeout={0}
                    unmountOnExit
                >
                    {_ => currentOutlet}
                </Transition>
            </SwitchTransition>
        </Suspense>
    );
}
condorheroblog commented 2 weeks ago

@southliu 请使用以下代码来实现吧

export const router = createBrowserRouter(...);

router.dispose();
// 相当于 vue-router 的 router.beforeEach 钩子
router.getBlocker("beforeGuard", onBeforeRouteChange);
// 相当于 vue-router 的 router.afterEach 钩子
router.subscribe(onAfterRouteChange);
southliu commented 2 weeks ago

tql大佬