next.js 源码解析 - getServerSideProps 篇

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 老规矩,昨天写了关于 getServerSideProps 的内容,今天趁热写一下 getServerSideProps 相应的源码,看看 next.js getServerSideProps 是怎么实现的,还有什么从文档无法知晓的细节。

网络异常,图片无法展示
|

老规矩,昨天写了关于 getServerSideProps 的内容,今天趁热写一下 getServerSideProps 相应的源码,看看 next.js getServerSideProps 是怎么实现的,还有什么从文档无法知晓的细节。

SSR 处理

我们先从 SSR 时相关的 getServerSideProps 处理看起,源码排查步骤上一步已经有所介绍,本篇不再多说,在 SSR 时,next.js 会调用 doRender 来进行渲染,其中会再次调用 renderHTML,进过各种判断和调用最终会进入 packages/next/server/render.tsx 中的 renderToHTML 进行处理。

// const SERVER_PROPS_ID = "__N_SSP";
if (getServerSideProps) {
    props[SERVER_PROPS_ID] = true;
}
复制代码

next.js 会先将 props 中的 SERVER_PROPS_ID 设置为 true,用做标识。

try {
    data = await getServerSideProps({
        req: req as IncomingMessage & {
            cookies: NextApiRequestCookies;
        },
        res: resOrProxy,
        query,
        resolvedUrl: renderOpts.resolvedUrl as string,
        ...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
        ...(previewData !== false ? { preview: true, previewData: previewData } : undefined),
        locales: renderOpts.locales,
        locale: renderOpts.locale,
        defaultLocale: renderOpts.defaultLocale
    });
    canAccessRes = false;
} catch (serverSidePropsError: any) {
    if (isError(serverSidePropsError) && serverSidePropsError.code === 'ENOENT') {
        delete serverSidePropsError.code;
    }
    throw serverSidePropsError;
}
if (data == null) {
    throw new Error(GSSP_NO_RETURNED_VALUE);
}
if ((data as any).props instanceof Promise) {
    deferredContent = true;
}
const invalidKeys = Object.keys(data).filter(key => key !== 'props' && key !== 'redirect' && key !== 'notFound');
if (invalidKeys.length) {
    throw new Error(invalidKeysMsg('getServerSideProps', invalidKeys));
}
复制代码

注意这里的 getServerSideProps 是从外层传进来了,因为涉及的代码较多,就不贴了,主要是通过 packages/next/server/load-components 中的 loadComponents,将路由文件中的 getServerSideProps 通过从 require 后的页面中取出。不过挺好奇他在 node 端是怎么 require 页面代码而不报错的,毕竟页面代码中很可能会存在依赖浏览器环境的代码,估计是做了一些类似于 runtime shim 之类的操作?此处先挖个坑,以后有空研究下。突然想起页面都是 SSR 了,初始化代码肯定都做过处理了 😂。

上面的代码可以看出 SSR 的时候是直接调用 getServerSideProps 传入 context 内容,context 的内容也一目了然。然后 next.js 会校验返回值是否为空,或者是否包含非法参数等。

然后回去检查 notFoundredirect 参数,进行特殊处理。

if ('notFound' in data && data.notFound) {
    if (pathname === '/404') {
        throw new Error(`The /404 page can not return notFound in "getStaticProps", please remove it to continue!`);
    }
    (renderOpts as any).isNotFound = true;
    return null;
}
if ('redirect' in data && typeof data.redirect === 'object') {
    checkRedirectValues(data.redirect as Redirect, req, 'getServerSideProps');
    (data as any).props = {
        __N_REDIRECT: data.redirect.destination,
        __N_REDIRECT_STATUS: getRedirectStatus(data.redirect)
    };
    if (typeof data.redirect.basePath !== 'undefined') {
        (data as any).props.__N_REDIRECT_BASE_PATH = data.redirect.basePath;
    }
    (renderOpts as any).isRedirect = true;
}
复制代码

此外从上面这段代码还发现一个有意思的就是 props 是可以为 Promise 的:

if ((data as any).props instanceof Promise) {
    deferredContent = true;
}
复制代码

返回 Promise 时,next.js 会在异常处理完毕后获取值:

if (deferredContent) {
    (data as any).props = await(data as any).props;
}
复制代码

最后 next.js 会将获取到的 props 值放入到顶层的 props 中:

props.pageProps = Object.assign({}, props.pageProps, (data as any).props);
(renderOpts as any).pageData = props;
复制代码

SSR 时,我们在页面中看到的用于 hydrate__NEXT_DATA__ 中的 props 就是这个 props,可以再看一眼其中的数据:

<script id="__NEXT_DATA__" type="application/json">
    {
        "props": {
            "pageProps": {
                "feature": {
                    "name": "xxxx",
                    "desc": "xxxx",
                    "tags": ["xxx"],
                    "id": "account-manage"
                }
            },
            "__N_SSP": true
        },
        "page": "/feature/[fid]",
        "query": { "fid": "account-manage" },
        "buildId": "development",
        "isFallback": false,
        "gssp": true,
        "scriptLoader": []
    }
</script>
复制代码

可以看到 pageProps__N_SSP 都是上面放进去的。

动态加载处理

看完了 SSR 场景下,next.js 如何处理 getServerSideProps,我们再看下页面为动态加载时的处理。

通过跳转时发起请求的调用栈,我们很轻松就能找到在页面为动态加载时,next.js 将会通过 packages/next/shared/lib/router.ts 中的 getRouteInfo 来获取要跳转的页面信息,然后会通过 routeInfo__N_SSP 判定是否要去获取数据:

const shouldFetchData = routeInfo.__N_SSG || routeInfo.__N_SSP;
if (shouldFetchData) {
    const { json, cacheKey: _cacheKey } = data?.json
        ? data
        : await fetchNextData({
              dataHref: this.pageLoader.getDataHref({
                  href: formatWithValidation({ pathname, query }),
                  asPath: resolvedAs,
                  locale
              }),
              isServerRender: this.isSsr,
              parseJSON: true,
              inflightCache: this.sdc,
              persistCache: !isPreview,
              isPrefetch: false,
              unstable_skipClientCache
          });
    return {
        cacheKey: _cacheKey,
        props: json || {}
    };
}
复制代码

然后通过 fetchNextData 来获取数据,而我们上文提到的 _next/data/development/{url}.json?{query} 这段 URL 就是由 formatWithValidation 构建生成的。

而请求发送后服务端的处理就七绕八绕逻辑太深了,这里不一一列举代码,简单说下:next.js 会通过 /_next/data/ 匹配请求判断是否是数据请求,如果是数据请求将会一样执行 SSR代码,然后可以理解为走的就是上面 SSR 初始化时的那套逻辑,只是最后会按照数据请求标识,将 props 抽离出来,放到响应中中返回。

总结

getServerSideProps 相关的源码还是有点绕的,其中应该还少了一些其它场景的相关代码,不过只看主场景应该就是这些了。

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
16天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
20天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
50 12
|
12天前
|
Web App开发 移动开发 HTML5
html5 + Three.js 3D风雪封印在棱镜中的梅花鹿动效源码
html5 + Three.js 3D风雪封印在棱镜中的梅花鹿动效源码。画面中心是悬浮于空的梅花鹿,其四周由白色线段组成了一个6边形将中心的梅花鹿包裹其中。四周漂浮的白雪随着多边形的转动而同步旋转。建议使用支持HTML5与css3效果较好的火狐(Firefox)或谷歌(Chrome)等浏览器预览本源码。
42 2
|
1月前
ractive.js联系表单动画效果源码
一款ractive.js联系表单动画效果,很有创意的发送邮件、联系内容等表单,基于ractive.js实现的动画效果,以发送信件的方式。
24 1
|
1月前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
1月前
|
前端开发 JavaScript
用HTML CSS JS打造企业级官网 —— 源码直接可用
必看!用HTML+CSS+JS打造企业级官网-源码直接可用,文章代码仅用于学习,禁止用于商业
124 1
|
1月前
|
JavaScript
JS趣味打字金鱼小游戏特效源码
hi fish是一款打字趣味小游戏,捞出海里的鱼,捞的越多越好。这款游戏用于电脑初学者练习打字。初学者可以根据自己的水平设置游戏难度。本段代码可以在各个网页使用,有需要的朋友可以直接下载使用,本段代码兼容目前最新的各类主流浏览器,是一款非常优秀的特效源码!
32 3
|
25天前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
17 0
|
25天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
54 0

推荐镜像

更多
下一篇
DataWorks