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

如何实现SEO友好的React分页?Link标签致组件不渲染

符合SEO要求的React分页问题及解决

我要做一个谷歌认可、符合SEO的分页功能,调研后决定用URL参数(比如/something?page=2,参考Facebook的用法),而不是状态管理,也认可/something/page/2但偏好前者。

现在遇到的问题:用<a>标签会整页刷新,效率低;改用React Router的<Link>标签后,分页失效——点击上下页链接时,目标组件DataList不重新渲染,但React DevTools显示其他组件会渲染,明显有问题。

分页组件代码

interface PaginatorProps {
    currentPage: number;
    itemCount: number;
    itemsPerPage?: number;
    path: string;
}

export const Paginator: FC<PaginatorProps> = ({
    currentPage,
    itemCount,
    itemsPerPage,
    path,
}) => {
    const totalPages: number = Math.ceil(itemCount / itemsPerPage);
    const disablePrev: boolean = currentPage <= 1;
    const disableNext: boolean = currentPage >= totalPages; // is last page (or 'above')

    if (totalPages <= 1 || !totalPages || !itemsPerPage || currentPage > totalPages) {
         return null;
    }

    let next: string = path,
        prev: string = path;

    if (currentPage + 1 <= itemCount) {
        next = `${path}?page=${currentPage + 1}`;
    }

    if (currentPage - 1 >= 1) {
        prev = `${path}?page=${currentPage - 1}`;
    }

    return (
        <div>
            <Link to={prev} className={`${disablePrev ? 'disabled' : ''}`}>
                Prev
            </Link>

            <span className={'paginationStyles.currentPage'}>
                Page {currentPage} of {totalPages}
            </span>

            <Link to={next} className={`${disableNext ? 'disabled' : ''}`}>
                Next
            </Link>
        </div>
    );
};

使用分页组件的示例代码

export const DataList = () => {
    const itemsPerPage: number = 3;
    const urlParams: URLSearchParams = new URLSearchParams(window.location.search);
    const currentPage: number = Number(urlParams.get('page')) || 1;
    const skip: number = (currentPage - 1) * itemsPerPage;
    const { dataCount, data, loading, error } = useGetDataList({ limit: itemsPerPage, skip });

    if (loading) return <Loading />;
    if (error) return <Error msg={error} />;
    if (!data?.length) return <p>No data found...</p>;

    return (
        <>
            <ul>
                {data.map(({ email }) => (
                    <li key={email}>{email}</li>
                ))}
            </ul>
            <Paginator
                currentPage={currentPage}
                itemCount={dataCount}
                itemsPerPage={itemsPerPage}
                path='/user/profile/affiliate/data-list'
            />
        </>
    );
};

问题根源

DataList里的currentPage是直接从window.location.search读取的,React Router通过<Link>跳转时只会更新路由状态,但组件不会感知到URL参数的变化——因为组件初始化时只读取了一次URL参数,后续参数变化不会触发组件重新渲染。

修复方案

1. 用React Router的useSearchParams监听参数变化

这是官方推荐的方式,能自动感知URL搜索参数的变化并触发组件重渲染,替代直接读取window.location:

import { useSearchParams } from 'react-router-dom';

export const DataList = () => {
    const itemsPerPage: number = 3;
    const [searchParams] = useSearchParams();
    const currentPage: number = Number(searchParams.get('page')) || 1;
    const skip: number = (currentPage - 1) * itemsPerPage;
    const { dataCount, data, loading, error } = useGetDataList({ limit: itemsPerPage, skip });

    if (loading) return <Loading />;
    if (error) return <Error msg={error} />;
    if (!data?.length) return <p>No data found...</p>;

    return (
        <>
            <ul>
                {data.map(({ email }) => (
                    <li key={email}>{email}</li>
                ))}
            </ul>
            <Paginator
                currentPage={currentPage}
                itemCount={dataCount}
                itemsPerPage={itemsPerPage}
                path='/user/profile/affiliate/data-list'
            />
        </>
    );
};

2. 修复Paginator的下一页判断逻辑

原代码中判断下一页的条件currentPage + 1 <= itemCount错误,应该和总页数比较,而非总条目数:

// 原错误代码
// if (currentPage + 1 <= itemCount) {
//     next = `${path}?page=${currentPage + 1}`;
// }

// 修改为
if (currentPage + 1 <= totalPages) {
    next = `${path}?page=${currentPage + 1}`;
}

3. 优化禁用状态的交互(可选)

当前的disabled类仅控制样式,点击禁用的链接仍会跳转,可添加阻止默认行为的逻辑:

<Link 
    to={prev} 
    className={`${disablePrev ? 'disabled' : ''}`}
    onClick={(e) => disablePrev && e.preventDefault()}
>
    Prev
</Link>

<Link 
    to={next} 
    className={`${disableNext ? 'disabled' : ''}`}
    onClick={(e) => disableNext && e.preventDefault()}
>
    Next
</Link>

方案合理性验证

你的SEO方案完全没问题——URL参数式分页是谷歌认可的,只要每个分页页面有独立内容,且<Link>生成可爬取的链接,爬虫就能正常遍历分页内容。使用useSearchParams既保留了无刷新跳转的优势,又能让组件响应URL变化,完美平衡SEO和用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 15:10:25