Next.js 路由跳转显示进度条:使用 BProgress 提升用户体验
本期已录制 B 站视频 👉 【Next.js】路由跳转显示进度条
哈喽,我是楷鹏。
先来看一个反面教材。
在 Dify.ai 中,当点击跳转页面之后,会有一段需要等待的时间,然后才会跳转页面。
然而,中间这段时间我并不知道是否跳转成功了,所以我会多点了几下,直到跳转。
这种体验很不好 👎
解决方案很简单,我们来看一下 GitHub 的跳转交互。
可以看到,GitHub 在跳转期间,会显示一个进度条,清晰地告诉用户——"我正在跳转,请稍等"。
那么在 Next.js 中,如何实现这个效果呢?
我们可以借助 BProgress 这个库来实现。
BProgress 是一个轻量级的进度条组件库,支持 Next.js 15+,同时也支持 Remix、Vue 等其他框架。
对于 BProgress 的使用,我做了一个 demo 项目 nextjs-progress-bar-demo,我们可以把这个项目先 clone 下来:
git clone git@github.com:wukaipeng-dev/nextjs-progress-bar-demo.git
然后进入项目目录:
cd nextjs-progress-bar-demo
先安装依赖:
npm install @bprogress/next
启动项目:
npm run dev
可以看到,这是一个简单的 Next.js 项目,包含三个页面:首页、登录页、注册页。
main
分支已经配置好了进度条,我们切换到分支 without-progress-bar-demo
:
git checkout without-progress-bar-demo
当前分支下,我们没有配置进度条,所以跳转页面时,不会显示进度条。
接下来我们在根布局 app/layout.tsx
中引入 ProgressProvider
:
'use client';
import "./globals.css";
import { ProgressProvider } from '@bprogress/next/app';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<ProgressProvider
height="4px"
color="#4c3aed"
options={{ showSpinner: false }}
shallowRouting
>
{children}
</ProgressProvider>
</body>
</html>
);
}
接下来,我们可以看一下,在首页和登录页、登录页和注册页之间跳转,都会显示一个进度条。
ProgressProvider
的参数如下:
height
:进度条的高度color
:进度条的颜色options
:进度条的配置,这里showSpinner
设置为false
,表示不显示一个动画的加载图标。shallowRouting
:是否启用浅层路由,如果开启的话,只改变路由的 query 参数,比如?page=1
变成?page=2
,那么进度条不会重新加载。
但是,当我们登录成功之后,再点击跳转,却不会显示进度条。
这是因为,首页和登录页、登录页和注册页之间,是使用 <Link>
组件进行跳转的。
<Link>
组件实际会渲染成 <a>
,BProgress 通过给所有 <a>
组件添加点击事件,来显示进度条。
我们可以看下在 DevTools → Elements → <a>
→ Event Listeners 中,是否添加了点击事件:
但是,当我们登录成功之后,则是使用 router.push
进行跳转的。
BProgress 不会给 router.push
添加点击事件,自然也不会显示进度条。
不用慌,BProgress 为我们提供了 useRouter
方法。
将 Next.js 的 useRouter
替换为 BProgress 提供的 useRouter
:
// import { useRouter } from 'next/navigation';
import { useRouter } from '@bprogress/next/app';
然后,正常使用即可:
const router = useRouter();
router.push('/');
这时,你可以看到,在登录成功之后,自动跳转首页时,进度条就能正常显示了。
但如果你的项目已经封装过了自己的 useRouter
,那么你可以将封装过的 useRouter
作为参数 customRouter
传入,进行二次封装:
import { useRouter } from '@bprogress/next/app';
import { useRouter as useNextIntlRouter } from '@/i18n/navigation';
export default function Home() {
const router = useRouter({
customRouter: useNextIntlRouter,
});
return (
<button
onClick={() =>
router.push('/about', {
startPosition: 0.3,
locale: 'en',
})
}
>
Go to about page
</button>
);
}
最后,让我们回到 app/layout.tsx
,这里我们引入了 ProgressProvider
,但却把 app/layout
变成了一个客户端组件,我们来把 ProgressProvider
抽离到其他地方,仍然保持 app/layout
是一个服务端组件。
// app/components/ProgressWrapper.tsx
'use client';
import { ProgressProvider } from '@bprogress/next/app';
interface ProgressWrapperProps {
children: React.ReactNode;
}
export function ProgressWrapper({ children }: ProgressWrapperProps) {
return (
<ProgressProvider
height="4px"
color="#0000ff"
options={{ showSpinner: false }}
shallowRouting
>
{children}
</ProgressProvider>
);
}
在 app/layout.tsx
中,我们引入 ProgressWrapper
:
import { ProgressWrapper } from './components/ProgressWrapper';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<ProgressWrapper>
{children}
</ProgressWrapper>
</body>
</html>
);
}
好的,不愧是你,完成了一个 Next.js 集成路由跳转显式进度条的封装。
以上就是本期的全部内容,希望对你有所帮助。
感谢观看!👏