MST

星途 面试题库

面试题:Next.js嵌套路由下针对动态数据的增量静态再生与加载策略优化

考虑一个Next.js应用,其嵌套路由结构中有页面展示动态变化的数据。使用增量静态再生(Incremental Static Regeneration)时,在多层嵌套路由的场景下,如何确保数据的一致性和高效加载?例如,当父路由参数变化时,如何避免子路由页面数据的重复请求或不一致,同时优化缓存策略以提高整体性能,请详细阐述设计思路和具体实现方法。
12.8万 热度难度
前端开发Next.js

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 理解增量静态再生原理:增量静态再生允许在页面构建后重新生成静态页面,在指定的时间间隔内或基于某些事件。
  2. 数据一致性
    • 共享数据层:使用共享的数据获取逻辑,确保父路由和子路由获取数据的来源一致。例如,可以创建一个公共的数据获取函数,在父路由和子路由页面中复用。
    • 依赖跟踪:当父路由参数变化时,建立一种机制来跟踪子路由页面所依赖的数据变化。可以通过上下文(Context)或者全局状态管理工具(如Redux)来实现。
  3. 高效加载
    • 缓存策略:利用HTTP缓存机制,设置合适的缓存头(如Cache - Control)。对于静态资源,可以设置较长的缓存时间;对于动态数据,根据其变化频率设置合理的缓存时间。
    • 避免重复请求:在客户端层面,使用数据缓存库(如swrreact - query),它们可以自动管理缓存,避免重复请求相同的数据。在服务端,使用中间件(如Next.jsnext - server - api - route - cache)来缓存API响应。

具体实现方法

  1. 共享数据获取函数
    • 在项目中创建一个lib/api目录,例如lib/api/fetchData.js
    export async function fetchMyData(params) {
        const response = await fetch(`https://example.com/api/data?${new URLSearchParams(params).toString()}`);
        return response.json();
    }
    
    • 在父路由页面(如pages/parent/[parentId].js)和子路由页面(如pages/parent/[parentId]/child/[childId].js)中导入并使用这个函数。
    import { fetchMyData } from '@/lib/api/fetchData';
    
    export async function getStaticProps({ params }) {
        const data = await fetchMyData({ parentId: params.parentId });
        return {
            props: {
                data
            },
            revalidate: 60 // 每60秒重新验证
        };
    }
    
    const ParentPage = ({ data }) => {
        // 页面渲染逻辑
    };
    
    export default ParentPage;
    
  2. 依赖跟踪
    • 使用上下文:创建一个上下文(Context)来传递父路由参数和相关数据状态。
    import { createContext } from'react';
    
    const ParentDataContext = createContext();
    
    export default ParentDataContext;
    
    • 在父路由页面中,将数据和参数通过上下文传递给子路由。
    import ParentDataContext from '@/context/ParentDataContext';
    
    export async function getStaticProps({ params }) {
        const data = await fetchMyData({ parentId: params.parentId });
        return {
            props: {
                data
            },
            revalidate: 60
        };
    }
    
    const ParentPage = ({ data }) => {
        return (
            <ParentDataContext.Provider value={{ data, parentId: data.parentId }}>
                {/* 子路由部分 */}
            </ParentDataContext.Provider>
        );
    };
    
    export default ParentPage;
    
    • 在子路由页面中,通过上下文获取数据,避免重复请求。
    import ParentDataContext from '@/context/ParentDataContext';
    import { useContext } from'react';
    
    const ChildPage = () => {
        const { data } = useContext(ParentDataContext);
        // 子路由页面使用数据渲染
        return (
            <div>
                {data.childData}
            </div>
        );
    };
    
    export default ChildPage;
    
  3. 缓存策略
    • HTTP缓存头:在next.config.js中设置缓存头。
    module.exports = {
        async headers() {
            return [
                {
                    source: '/api/:path*',
                    headers: [
                        {
                            key: 'Cache - Control',
                            value: 'public, max - age = 3600'
                        }
                    ]
                }
            ];
        }
    };
    
    • 客户端缓存:使用swr库。首先安装swrnpm install swr
    import useSWR from'swr';
    import { fetchMyData } from '@/lib/api/fetchData';
    
    const ChildPage = () => {
        const { data, error } = useSWR(['/api/data', { parentId: '123' }], fetchMyData);
        if (error) return <div>Error loading data</div>;
        if (!data) return <div>Loading...</div>;
        return (
            <div>
                {data.childData}
            </div>
        );
    };
    
    export default ChildPage;
    
    • 服务端缓存:使用next - server - api - route - cache库。安装:npm install next - server - api - route - cache。在API路由中使用:
    import nc from 'next - connect';
    import { cache } from 'next - server - api - route - cache';
    import { fetchMyData } from '@/lib/api/fetchData';
    
    const handler = nc();
    
    cache(handler, {
        cacheControl: 'public, s - maxage = 3600, stale - while - revalidate = 60',
        keys: (req) => [req.query.parentId]
    });
    
    handler.get(async (req, res) => {
        const data = await fetchMyData({ parentId: req.query.parentId });
        res.json(data);
    });
    
    export default handler;