You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中从SSR页面路由到CSR页面避免短暂闪显问题

解决SSR页面跳转CSR页面时的短暂闪显问题

针对你遇到的SSR页面跳转同外观CSR页面时的闪显问题,这里有几个不用加载器就能搞定的方案:

核心原因

这种闪显大多是因为CSR页面完成hydration(客户端渲染)前,旧SSR页面的DOM还没完全移除,或者新页面初始渲染的内容和旧页面有差异,导致视觉跳变。


1. 点击跳转时立即隐藏旧页面内容

在触发路由跳转的瞬间,通过状态控制让当前SSR页面的内容快速隐藏,配合过渡动画让过程更平滑,用户根本察觉不到闪显。

示例代码:

import { useRouter } from 'next/router';
import { useState } from 'react';

export default function SSRPage() {
  const router = useRouter();
  const [isNavigating, setIsNavigating] = useState(false);

  const goToCSRPage = () => {
    // 点击后立刻标记为跳转中,隐藏当前页面
    setIsNavigating(true);
    router.push('/csr-page').finally(() => {
      // 路由完成后重置状态,避免意外情况
      setIsNavigating(false);
    });
  };

  return (
    <div style={{ 
      opacity: isNavigating ? 0 : 1, 
      transition: 'opacity 0.2s ease-out',
      pointerEvents: isNavigating ? 'none' : 'auto' // 防止跳转中误触
    }}>
      {/* 你的SSR页面内容 */}
      <button onClick={goToCSRPage}>跳转到CSR页面</button>
    </div>
  );
}

2. 抽离共享布局/骨架组件

既然两个页面外观相似,把公共的布局、静态骨架抽成独立组件,让CSR页面在加载数据前先渲染和SSR页面完全一致的骨架内容,这样路由切换时视觉上几乎没有变化。

示例:

// 公共布局组件 components/SharedPageFrame.js
export default function SharedPageFrame({ children }) {
  return (
    <>
      <header className="common-header"> {/* 公共头部 */}</header>
      <main className="common-container">{children}</main>
      <footer className="common-footer"> {/* 公共底部 */}</footer>
    </>
  );
}

SSR页面直接用:

// pages/ssr-page.js
import SharedPageFrame from '../components/SharedPageFrame';

export async function getServerSideProps() {
  // 服务端数据请求逻辑
  return { props: { data: ... } };
}

export default function SSRPage({ data }) {
  return (
    <SharedPageFrame>
      {/* SSR页面的内容,和CSR页面的骨架一致 */}
      <div className="content-block">{data.content}</div>
    </SharedPageFrame>
  );
}

CSR页面先渲染骨架,等数据加载完成再替换:

// pages/csr-page.js
import { useState, useEffect } from 'react';
import SharedPageFrame from '../components/SharedPageFrame';

export default function CSRPage() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // 客户端数据请求
    fetch('/api/get-data')
      .then(res => res.json())
      .then(setData);
  }, []);

  return (
    <SharedPageFrame>
      {/* 先渲染和SSR页面完全一样的骨架 */}
      {data ? (
        <div className="content-block">{data.content}</div>
      ) : (
        <div className="content-block"> {/* 和SSR页面相同的静态占位 */}</div>
      )}
    </SharedPageFrame>
  );
}

3. 预加载CSR页面资源

在SSR页面中提前预加载CSR页面的JS文件,让跳转时资源已经就绪,缩短CSR页面的hydration时间,从根源上减少闪显的可能。

示例代码:

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function SSRPage() {
  const router = useRouter();

  useEffect(() => {
    // 页面加载完成后,后台预加载CSR页面的资源
    router.prefetch('/csr-page');
  }, [router]);

  return (
    <button onClick={() => router.push('/csr-page')}>跳转到CSR页面</button>
  );
}

4. 临时禁用SSR页面的卸载动画

如果你的SSR页面加了组件卸载动画,可能会和CSR页面的渲染重叠导致闪显,可以在路由开始切换时禁用动画:

import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

export default function SSRPage() {
  const router = useRouter();
  const [isLeaving, setIsLeaving] = useState(false);

  useEffect(() => {
    const startRouteChange = () => setIsLeaving(true);
    router.events.on('routeChangeStart', startRouteChange);
    return () => router.events.off('routeChangeStart', startRouteChange);
  }, [router]);

  return (
    <div className={isLeaving ? 'no-transition' : 'page-transition'}>
      {/* 页面内容 */}
    </div>
  );
}

对应的CSS:

.page-transition {
  transition: all 0.3s ease;
}
.no-transition {
  transition: none !important;
}

内容的提问来源于stack exchange,提问作者sdVak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 06:28:24