next.js getServerSideProps源码解析

目录
  • SSR 处理
  • 动态加载处理
  • 总结

SSR 处理

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

我们先从 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 相关的源码还是有点绕的,其中应该还少了一些其它场景的相关代码,不过只看主场景应该就是这些了。

以上就是next.js getServerSideProps源码解析的详细内容,更多关于next.js getServerSideProps的资料请关注我们其它相关文章!

(0)

相关推荐

  • Next.js脚手架完整搭建封装的方法步骤

    针对实际的开发场景(SEO优化需求),我们直接使用next.js脚手架创建的项目还无法直接进行开发,需要再次进行配置封装搭建,这里分享一套自己的完整封装搭建给有需要的小伙伴使用; 内容包括:(1)sass样式配置;(2)axios拦截封装;(3)action模块化;(4)reducer模块化;(5)redux搭建;(6)dispatch示范;(7)saga中间件配置;(8)saga拦截示范;(9)useEffect异步请求示范;(10)getServerSideProps/getStaticPr

  • react框架next.js学习之API 路由篇详解

    目录 正文 使用方式 API 路由匹配 API 处理 API 配置 边缘计算支持 自定义 API 注意点 总结 正文 next.js 作为最热门的 react 框架,不过这么久了好像国内使用率一直不太高.最近在研究做个小项目正好做下笔记,有兴趣的可以一起来学习. next.js 首页标榜的 12 个特性之一就是 API routes,简单的说就是可以 next.js 直接写 node 代码作为后端服务来运行.因此我们可以直接使用 next.js 直接维护一个全栈项目,听起来很香的样子. 使用方式

  • Next.js入门使用教程

    目录 简介 创建Next.js项目 手动创建Next.js项目 creact-next-app快速创建项目 Pages 路由 Link Router 参数传递与接收 动态路由 钩子事件 获取数据 getStaticProps 构建时请求数据 getServerSideProps 每次访问时请求数据 CSS支持 添加全局样式表 添加组件级CSS 简介 Next.js 是一个轻量级的 React 服务端渲染应用框架. 官网链接:www.nextjs.cn/ 优点: 零配置 自动编译并打包.从一开始就

  • 基于Next.js实现在线Excel的详细代码

    目录 认识 Next.js 实战之旅 如果要从头开始使用 React 构建一个完整的 Web 应用程序,需要哪些步骤?这当然不像把大象装进冰箱那么简单,只需要分成三步:打开冰箱,拿起大象,塞进冰箱就好. 我们需要考虑细节有很多,比如: 必须使用打包程序(例如 webpack)打包代码,并使用 Babel 等编译器进行代码转换. 需要针对生产环境进行优化,例如代码拆分.需要对一些页面进行预先渲染以提高页面性能和 SEO,可能还希望使用服务器端渲染或客户端渲染. 必须编写一些服务器端代码才能将 Re

  • next.js初始化参数设置getServerSideProps应用学习

    目录 使用 ts 定义 context 实现 请求 API 问题 特殊处理 - 404.跳转.异常 总结 使用 getServerSideProps 是 next.js 中的一项特色功能,可以让我们在给页面设置一些初始的 props 参数. getServerSideProps 是定义在页面中的 API,但是其执行环境是 node 端,而不是客户端,一般常见使用场景为: 页面前置权限校验 页面必备参数获取 使用时需要在对应的 page 文件中 export getServerSideProps

  • 使用next.js开发网址缩短服务的方法

    一.网址缩短服务的原理 网址缩短服务,并不是压缩算法.而是把原网址存储在数据库中,用短的参数做key,届时取出原始url,并跳转. 因此,短网址最适合用key/value数据库. 那么,短网址的唯一参数,如何生成呢?其实用的就是10进制转62进制. function string10to62(number) { var chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'.split(''), radix

  • next.js getServerSideProps源码解析

    目录 SSR 处理 动态加载处理 总结 SSR 处理 老规矩,昨天写了关于 getServerSideProps 的内容,今天趁热写一下 getServerSideProps 相应的源码,看看 next.js getServerSideProps 是怎么实现的,还有什么从文档无法知晓的细节. 我们先从 SSR 时相关的 getServerSideProps 处理看起,源码排查步骤上一步已经有所介绍,本篇不再多说,在 SSR 时,next.js 会调用 doRender 来进行渲染,其中会再次调用

  • solid.js响应式createSignal 源码解析

    目录 正文 createSignal readSignal writeSignal 案例分析 总结 正文 www.solidjs.com/docs/latest… createSignal 用来创建响应式数据,它可以跟踪单个值的变化. solid.js 的响应式实现参考了 S.js,它是一个体积超小的 reactive 库,支持自动收集依赖和简单的响应式编程. createSignal createSignal 首先我们来看下 createSignal 的声明: // packages/soli

  • jq源码解析之绑在$,jQuery上面的方法(实例讲解)

    1.当我们用$符号直接调用的方法.在jQuery内部是如何封装的呢?有没有好奇心? // jQuery.extend 的方法 是绑定在 $ 上面的. jQuery.extend( { //expando 用于决定当前页面的唯一性. /\D/ 非数字.其实就是去掉小数点. expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready wit

  • AngularJS动态生成div的ID源码解析

    1.问题背景 给定一个数组对象,里面是div的id:循环生成div元素,并给id赋值 2.实现源码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>AngularJS动态生成div的ID</title> <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angula

  • 详解vue mint-ui源码解析之loadmore组件

    本文介绍了vue mint-ui源码解析之loadmore组件,分享给大家,具体如下: 接入 官方接入文档mint-ui loadmore文档 接入使用Example html <div id="app"> <mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" :max-dis

  • 浅谈Vuejs中nextTick()异步更新队列源码解析

    vue官网关于此解释说明如下: vue2.0里面的深入响应式原理的异步更新队列 官网说明如下: 只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变.如果同一个 watcher 被多次触发,只会一次推入到队列中.这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要.然后,在下一个的事件循环"tick"中,Vue 刷新队列并执行实际(已去重的)工作.Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MutationOb

  • vue 源码解析之虚拟Dom-render

    vue 源码解析 --虚拟Dom-render instance/index.js function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } renderMixin

  • Vue源码解析之数组变异的实现

    力有不逮的对象 众所周知,在 Vue 中,直接修改对象属性的值无法触发响应式.当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变. 这是什么原因? 原因在于: Vue 的响应式系统是基于Object.defineProperty这个方法的,该方法可以监听对象中某个元素的获取或修改,经过了该方法处理的数据,我们称其为响应式数据.但是,该方法有一个很大的缺点,新增属性或者删除属性不会触发监听,举个栗子: var vm = new Vue({ data () { return

  • 从vue源码解析Vue.set()和this.$set()

    前言 最近死磕了一段时间vue源码,想想觉得还是要输出点东西,我们先来从Vue提供的Vue.set()和this.$set()这两个api看看它内部是怎么实现的. Vue.set()和this.$set()应用的场景 平时做项目的时候难免不会对 数组或者对象 进行这样的骚操作操作,结果发现,咦~~,他喵的,怎么页面没有重新渲染. const vueInstance = new Vue({ data: { arr: [1, 2], obj1: { a: 3 } } }); vueInstance.

  • react diff算法源码解析

    React中Diff算法又称为调和算法,对应函数名为reconcileChildren,它的主要作用是标记更新过程中那些元素发生了变化,这些变化包括新增.移动.删除.过程发生在beginWork阶段,只有非初次渲染才会Diff. 以前看过一些文章将Diff算法表述为两颗Fiber树的比较,这是不正确的,实际的Diff过程是一组现有的Fiber节点和新的由JSX生成的ReactElement的比较,然后生成新的Fiber节点的过程,这个过程中也会尝试复用现有Fiber节点. 节点Diff又分为两种

随机推荐