入口这是一个基础demo,服务端由开发者提供用于渲染constkoa=require('koa')constRouter=require('koa-router')constnext=require('next')//创建实例constapp=next({dev,conf,dir:'./src'})app.prepare().then(()=>{constserver=newKoa()constrouter=newRouter()router.get('/',asyncctx=>{//renderawaitapp.render(ctx.req,ctx.res)ctx.respond=false})server.listen(port)})在自定义服务器next()中传递constapp=创建实例并使用app.render(req,res)方法进行渲染,所以直接从app.render渲染入口入手了解框架逻辑只能看源码。由于源码过于详细,下面我会简化一下。代码,只保留主要逻辑,有具体地址,有兴趣的同学可以看看app.rendernext-server/server/next-server.tsimport{renderToHTML}from'./render.tsx'//app.render入口函数this.render(req,res){consthtml=awaitthis.renderToHTML(req,res)returnthis.sendHTML(req,res,html)}this.renderToHTML(req,res){consthtml=awaitthis.renderToHTMLWithComponents(req,res)returnhtml}this.renderToHTMLWithComponents(req,res){//渲染中的renderToHTMLreturnrenderToHTML(req,res)}可以看到上面是一个简单的调用关系。虽然删除了大部分代码,但我们只需要知道最后它在render.tsx中调用了renderToHTML即可。这是一个比较长的函数,也就是本文的主要内容,通过renderToHTML可以了解大部分内容,同上,删除大部分逻辑,只保留核心代码renderToHTML//next-server/server/render.tsxfunctionrenderToHTML(req,res){//参考下面#SupplementloadGetInitialProps,一个很简单的函数,就是调用_app.getInitialProps//_app.getInitialProps函数会先调用pages.Component的getInitialProps//这也是我们写的组件中的getInitialProps也会被调用的地方Call来获取一些初始数据letprops=awaitloadGetInitialProps(App,{Component,router,ctx});//定义渲染函数,返回html和headconstrenderPage=()=>{//参考下面#supplementrenderreturnrender(renderToStaticMarkup,//render_app,其内部pages.Component是我们写的代码,对于详情参考next/pages/_app.tsx);};//_document.getInitialProps会调用renderPage,rendering_app就是我们平时开发时写的组件代码。详情参考next/pages/_app.tsxconstdocProps=awaitloadGetInitialProps(Document,{...ctx,renderPage});//参考以下#补充renderDocumentlethtml=renderDocument(Document,{props,docProps,});returnhtml;}摘要req=>render(req,res)renderToHTML(req,res)renderToHTMLWithComponents(req,res)renderToHTML(req,res)_app.initialProps=loadGetInitialProps(App,{Component,router,ctx})_document.initialProps=loadGetInitialProps(Document,{...ctx,renderPage})renderDocument(Document,_app.initialProps,_document.initialProps)<=res对应req=>_app.getInitialProps()Component.getInitialProps()_document.getInitialProps()_app.render()Component.render()_document.render()<=res这篇文章简单描述下服务器的渲染过程,从中我们可以也清楚了_document、_app、pages中自己写的组件之间的关系……如果还不明白,请重新阅读renderToHTML函数中注释中的一些注意事项。补充_document只在服务器端执行。react提供的renderToString函数只产生html,是纯字符串,所有数据必须在调用renderToString前注入。在浏览器端渲染时,存在isInitialRender表示是否是第一次渲染。如果是第一次渲染,ReactDOM.hydrat将被调用e(reactEl,domEl)来执行绑定事件,所以生命周期的一部分(componentWillMount之后)和事件会在浏览器端执行。补充上面出现的一些函数,直接从源码中截取,比较简单,可以作为参考,ampMode:任何,):{html:字符串;head:React.ReactElement[]}{让html让head试试{html=renderElementToString(element)}最后{head=Head.rewind()||defaultHead(undefined,isAmp(ampMode))}return{html,head}}loadGetInitialPropsexport异步函数loadGetInitialProps(Component:NextComponentType,ctx:C):承诺{if(process.env.NODE_ENV!=='production'){if(Component.prototype&&Component.prototype.getInitialProps){constmessage=`"${getDisplayName(Component)}.getInitialProps()"已定义作为实例方法-访问https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method获取更多信息。`thrownewError(message)}}//当从_app调用时,`ctx`嵌套在`ctx`中constres=ctx.res||(ctx.ctx&&ctx.ctx.res)if(!Component.getInitialProps){returnnull}constprops=awaitComponent.getInitialProps(ctx)if(res&&isResSent(res)){returnprops}renderDocumentfunctionrenderDocument(文档:DocumentType,{...很多参数,太长省略}):string{return(''+renderToStaticMarkup(,))}