next.js 的服务端渲染机制(一)

前后端同构,作为针对单页应用 SEO 优化乏力、首屏速度瓶颈等问题而产出的解决方案,近来在 react、vue 等前端技术栈中都得到了支持。当我们正打算抛弃传统的纯服务端渲染模式,拥抱前后端分离的最佳实践时,有些人却已经从单页应用的格局里跳出,重新去思考和定义服务端渲染。

前后端同构是指在前后端维护同一份代码。它是在SPA的基础上,利用服务端渲染(SSR)直出首屏,解除单页面应用(SPA)在首屏渲染上面临的窘境。明确地说,同构是将传统的纯服务端直出的首屏优势和SPA的站内体验优势结合起来,以取得最优解的解决方案。

next.js 的服务端渲染机制(一)_第1张图片
两种渲染方式

next.js 是基于 react 的优秀的同构直出方案,结合 webpack 和自身提供的路由机制,可以说是开箱即用。近期由于项目技术栈迁移,接触到了 next,顺便也把 next 的源码看了一遍,所以抽空记一记。对 next 或者同构不太了解的建议先移步 next.js。next 的核心代码在于定制了一套 react 的服务端渲染方案,所以以下也是按照这个流程分步剖析源码。

目录结构一览
next.js 的服务端渲染机制(一)_第2张图片
next 源码 server 目录

next.js 的服务端渲染机制(一)_第3张图片
next 源码 lib 目录

next 服务端渲染的核心代码位于 server 目录下,完成了请求监听、路由分发、组件渲染和请求回馈等多个环节,lib 中的模块一部分是 next 自身使用(如app.js),另一部分是暴露给开发者的可用模块(如dynamic.js)。

如何启动服务

next 提供了它自己的 CLI,开发模式下我们直接通过 nextnext dev 执行 bin 目录下的 bin/next脚本。它通过你传入的参数去判别不同的操作,然后分发执行同目录下对应的其他脚本,当我们执行next时,bin/next会对应执行bin/next-dev

next.js 的服务端渲染机制(一)_第4张图片
next 源码 bin 目录

bin/next-dev中,根据代码我们可以猜想 next 完成了一个关键的操作:实例化一个服务对象,然后启动监听 http 请求。而同样的,在 bin/next-start中我们也能看到相同的代码。

next.js 的服务端渲染机制(一)_第5张图片
next.js 的服务端渲染机制(一)_第6张图片
bin / next-dev
document 请求时,如何完成服务端渲染

注意到启动服务时引入的的模块server/index.js,我们发现这个模块 export 了一个拥有许多方法的丰富的 server 类,不难意识到就是这个类完成了大部分的渲染工作。实际上不仅如此,这个 server 类兼具组件渲染、路由匹配、错误机制、缓存处理等等多个环节的实现,服务端的核心功能都浓缩在这个类上。

next.js 的服务端渲染机制(一)_第7张图片
server / index.js

而在 bin/next-dev执行的start()方法中,this.prepare()会根据实例化 server 对象传入的dev字段来判断是否启动hot-reloader,这个不做深入。关键的,我们可以看到之前的猜测是正确的,这里 next 利用node原生内置的http模块启动了一个服务,并传入了监听的回调函数。

next.js 的服务端渲染机制(一)_第8张图片
server / index.js

追溯这个回调函数的本体,我们定位到handleRequest这个函数,它首先对请求的 Url 做了一层处理,然后调用了run()方法,并将处理后的参数传了进去。

next.js 的服务端渲染机制(一)_第9张图片
server / index.js

而在run()方法里边,除了根据 dev 去运行hot-reloader和错误处理之外,next 做了一个路由的匹配。那么这个路由又是哪来的呢?
next.js 的服务端渲染机制(一)_第10张图片
server / index.js

基于 page 目录路径的路由匹配是如何实现的

定位一下不难发现,在实例化 server 对象(construtor())的时候,next 调用了它的defineRoutes()方法,在这个方法中定义了一个 routes 对象。这个对象是一系列路由和回调函数的映射。

next.js 的服务端渲染机制(一)_第11张图片
server / index.js

而定义这个对象后,next 又为这个对象增加多一个路由处理,并且将这个映射集合一并添加到this.router上。增加的这个路由实际上就是我们的页面 URL 匹配(划重点,这里是页面请求的入口),而this.router是 next 自身实现的路由器,这里只是做一个简单的路由登记。
next.js 的服务端渲染机制(一)_第12张图片
server / index.js

看回run()方法,正是服务启动时做了路由登记,在这里才能执行路由查询,并且执行了相应的回调。当然如果不存在回调,说明 URL 无效,自然是返回404咯。
next.js 的服务端渲染机制(一)_第13张图片
server / index.js

为了弄清楚 URL 的 path 是如何对应到文件路径,我们继续深入。看回刚刚我划了重点的地方,我们可以看到页面请求时执行的回调中,调用了一个 render()方法。在其中,next 除了一些常规的错误处理之外,最重要的是:1、调用了renderToHTML()方法; 2、调用了sendHTML()函数。可想而知这个步骤分别完成了页面渲染和请求回馈两个环节,而追溯这两个函数,可以定位到都来自于同目录下的server/render.js模块。

next.js 的服务端渲染机制(一)_第14张图片
image.png

未完,后续请看 next.js 的服务端渲染机制(二)

你可能感兴趣的:(next.js 的服务端渲染机制(一))