实现ssr服务器渲染的方法和步骤
前言
前段时间想做个人网站,然后马上行动。如何实现自己的网站,选择什么样的技术方案,由你自己决定。就在之前,我想过在服务器端渲染,加载速度快,seo也挺适合个人网站的。所以我自己搭建了轮子,用koa react实现了ssr服务器渲染。
什么是ssr
当我第一次听说有单页服务器端渲染时,它被理解为类似于传统的服务器端路由模板渲染,但它只需要在单页应用程序的框架中编写。以后再想这个似乎有点傻。再次证明,只有在第一次加载时,后端才会对当前路径页面进行组件渲染和数据请求,组装成html返回给前端,让用户快速看到页面。当加载html格式的js资源时,剩下的就是一般的单页应用程序。因此,ssr是后端模板呈现和单页的结合。ssr有两种模式,单页模式和非单页模式。第一种是首次在后端呈现的单页应用,第二种是完全使用后端路由的后端模板呈现模式。它们在使用后端路由的程度上有所不同。
优势
ssr的两个明显优势:快速首次加载和搜索引擎优化。为什么第一次加载快?对于一个普通的单页应用,第一次加载的时候,需要加载所有相关的静态资源,然后核心js开始执行,这需要一定的时间,然后会请求网络接口,最后完全完成渲染。
在ssr模式下,后端截取路由,找到相应的组件,并准备呈现该组件。所有js资源都是本地的,这不包括js资源的网络加载时间。然后只需要渲染当前路由的组件,页面的ajax请求可能在同一个服务器上,如果是,速度会快很多。最后,后端将呈现的页面返回到前端。注意:页面可以快速显示,但是由于当前返回的只是简单显示的dom和css,并且客户端上没有绑定js相关的事件,所以需要在加载js后重新渲染当前页面,这就叫做同构。因此,ssr是为了更快地显示页面内容,让用户先看到。为什么seo友好,因为在抓取页面信息的时候,搜索引擎爬虫会发送HTTP请求来获取web内容,而我们服务器第一次渲染的数据是后端返回的,返回的时候标题、内容等信息已经渲染好了,方便爬虫抓取内容。
如何实施
对安全部门改革有了一个大致的了解,我们现在需要理清实施的总体思路和流程。
1.选择单页框架(我目前选择反应)
2.选择节点服务器框架(koa2是我目前的选择)
3.实现核心逻辑,以便节点服务器可以路由和呈现单页组件(这被分成许多小的实现点,这将在后面描述)
4.优化开发和发布环境自动化构建工具(webpack)
在实施前创建一个react-ssr项目。在项目下,创建用于编写客户端和服务器代码的客户端和服务器目录,以及用于weppack文件配置的webpack目录。
1 .反应应用
安装reat依赖项,在客户端创建一个基本的reat文件夹结构,并编写一个具有路由配置的可运行应用程序。客户端文件目录如下:
2 .服务器应用程序
安装koa和相关的依赖项,在server中创建一个基本的服务器端文件夹结构,编写一个简单的可操作的后端应用服务。服务器文件夹如下:
3.核心实现
因为有仓库代码,基本代码就不解释了。现在我们有一个react单页应用程序和一个可以独立运行的后端应用程序,它们都有自己的路由。接下来我们做转换实现ssr的单页模式(非单页模式只是部分调整,这里只说实现单页模式)。
核心实现分为以下步骤:
1)后端拦截该路由,并根据该路由找到要呈现的react page组件x
2)调用组件X初始化时需要请求的接口。同步获取数据后,使用react的renderToString方法渲染组件,这样就可以渲染节点字符串了。
3)后端获取基础html文件,将渲染后的节点字符串插入正文,还可以操作标题、脚本等节点。向客户端返回完整的html。
4)客户端获取后端返回的html,在其中显示并加载js,最终完成react同构。
1)当我们在客户端写react时,路由器会定义一个数组来存储组件和对应的路径,然后注册路由,如下所示:
如上所述,ssr是单页应用的第一个服务器渲染,所以我们是单页应用。既然已经实现了单页应用,那么就需要实现第一次服务器端渲染。服务器的应用启动后,接收url请求,如访问http://localhost:9999/,后端服务获取当前路径为/。此时,我们希望后端找到上图中配置路径为“/”的Index组件并进行渲染。在客户端的路由器文件夹中创建了两个js文件索引和页面:
Pages配置路由路径和组件之间的映射,代码大致如下,以便客户端路由和服务器路由都可以使用。
服务器路由中的代码大致如下。服务器获得get请求后,匹配路径。如果路径有一个映射的页面组件,获取这个组件并呈现它。这是我们的第一步:后端拦截路由,并根据路径找到需要呈现的react page组件。
2)如上图所示,匹配组件后,实现组件的getInitialProps方法(与nextjs的命名一致)。这个方法是一个打包的静态方法,主要用于获取初始化所需的ajax数据,这些数据会在服务器端同步获取,然后通过ssrData参数传递到组件prorps中,执行组件渲染。这个方法在客户端仍然是异步请求。这一步更重要。为什么我们需要一个静态方法,而不是直接在willmount中编写请求?因为当服务器使用renderToString来呈现组件时,生命周期将只执行到willmount之后的第一次呈现。在willmount中,请求是异步的。第一次渲染完成时,没有得到异步数据,此时渲染Tostring已经返回。那么我们页面的初始化数据就没了,返回的html也不是我们期望的那样。因此,定义了一个静态方法,它在组件实例化之前获取,并同步执行。数据采集完成后,数据通过道具传输到组件进行渲染。那么这个方法是如何实现的呢?我们根据代码截图来看base.js:
首先,在客户端的页面中创建一个新的基础组件。基础继承React。组件页面中的所有页面组件都需要继承此基础。base有一个静态方法getInitialProps,主要返回组件初始化所需的异步数据。如果有一个初始化的ajax请求,应该在这个方法中重写它并返回数据对象。构造函数判断页面组件是否有初始化定义的state static方法,如果有,则将其传递给组件实例化的state对象。如果props传入ssrData,它会将ssrData值传递给组件状态对象。基础中的组件将决定是否需要执行getInitialProps方法。如果在服务器渲染过程中,数据在组件实例化之前已经被同步获取并传递到props中,它将被忽略。在客户端环境下,有两种情况,第一种:当用户第一次进入页面时,是服务器请求的数据。服务器获取数据后,在服务器上呈现组件,同时还将数据存储在html脚本代码中,以定义全局变量ssrData。如下图所示,当注册单页应用程序并同构时,react会将全局ssrData传递给页面组件。此时,当页面组件在客户端同构呈现时,它们可以继续使用服务器端之前的数据,从而保持同构一致性,避免重复请求。在第二种情况下,当前用户在单个页面中切换路由,因此没有服务器端呈现。然后,执行getInitialProps方法并将数据直接返回到状态,这几乎相当于在willmount中执行请求。这样,我们可以使用一组代码来兼容服务器端呈现和单页呈现。
client/app.js
看看如何编写页面组件。下面是页面组件Index的截图,它继承了Base并定义了一个静态。组件构造函数方法将把这个对象传递给组件实例化的状态对象。之所以使用静态方法写入默认数据,是为了确保先将定义的默认状态传递给实例对象的状态,然后将接口请求的props数据传递给实例对象的状态。为什么不直接写state属性,加上state,因为state属性会在构造函数之后执行,这会覆盖构造函数定义的状态,也就是我们getInitialProps返回的数据。
注意:在服务器端的渲染环境中,当renderToString被执行时,组件将被实例化,字符串形式的dom将被返回。在此过程中,react组件的生命周期将仅在willmount之后执行。
3)我们编写一个html文件,大致如下。目前已经渲染了对应的节点字符串,后端需要返回html文本,应该包括标题、节点和最后要加载的打包好的js,依次替换html占位符部分。
index.html
服务器/路由器. js
4)最后,当客户端js被加载时,它将运行react并执行同构方法ReactDOM.hydrate,而不是ReactDOM.render
以下是第一个渲染过程的一般流程图。单击以查看更大的图像
Css处理
现在我们已经完成了核心逻辑,但是有一个问题。我发现在后端呈现组件时,style-loader会报告错误,并找到组件所依赖的css,在加载组件时将style加载到html头中。但是,当我们在服务器端渲染时,没有窗口对象,所以style-loader的内部代码会报告错误。服务器上的webpack需要移除样式加载器并用其他方法替换它。稍后,我将样式分配给组件的静态变量,然后在服务器上呈现它并将其返回给前端。但是,有一个问题。我只能得到当前组件的样式,不能得到子组件的样式。如果我想给子组件添加一个静态方法,那就太麻烦了。后来我找到了一个可以支持我们想要的功能的库同构样式加载器,看了看它的源代码和用法,通过高阶函数给组件分配样式,然后利用react Context得到所有需要渲染的组件的样式,最后将样式插入到html中,解决了子组件的样式无法导入的问题。但是我觉得有点麻烦。首先,我们需要定义所有组件的高阶函数,并介绍这个库。然后,我们需要在路由器中编写相关代码来收集样式,最后插入到html中。后来,我定义了一个ProcessSsrStyle方法。输入参数为样式文件,逻辑为判断环境。如果服务器将样式加载到当前组件的dom中,那么如果它是客户端,它将不会被处理(因为客户端有样式加载器)。实现和使用非常简单,如下所示:
ProcessSsrStyle.js
使用:
服务器返回的html内容如下。用户可以立即看到完整的页面样式。当客户端react同构完成后,dom会被纯dom替代,因为ProcessSsrStyle方法不会在客户端输出样式,最后执行style-loader后会在头部有样式,所以页面不会有不一致的变化,对用户不敏感。
至此,核心功能已经实现,但是在后来的开发中,我发现事情并没有那么简单,因为开发环境似乎太不友好,开发效率低,需要手动重启。
发展环境
让我们来谈谈初始开发环境是如何工作的:
npm run dev启动开发环境webpack.client-dev.js来打包服务器端代码,这些代码将打包在dist/server中。webpack.server-dev.js打包客户端代码,代码将打包在dist/client中启动服务器端应用程序。webpack-dev-server在端口9999启动,webpack在端口8888打包后启动两个服务。一个是服务器端的app应用,端口9999,另一个是客户端的dev-server,端口8888。开发服务器将监控并打包客户端代码,当客户端代码更新时,它可以实时更新前端代码。访问localhost:9999时,服务器会返回html,我们服务器返回的html中的js脚本路径就是指向的dev-serve端口的地址,如下图所示。也就是说,客户端程序和服务器程序是分开打包的,运行两种不同的端口服务。
在生产环境中,因为不需要dev-server来监控和热更新,所以只有一个服务就足够了,如下图所示,服务器注册静态资源文件夹:
服务器/app.js
当前的构建系统区分了生产环境和开发环境,现在构建开发环境没有问题。但是开发环境问题很明显,最大的问题就是服务器没有热更新或者重新打包重启。这会导致很多问题,最严重的是前端已经更新了组件,但是服务器还没有更新,所以同构的时候会出现不一致的情况,从而导致错误,有些错误会影响操作,唯一的解决办法就是重启。这样的发展经历让人无法忍受。后来开始考虑在服务器端做热更新。
监控、打包、重启
起初,我的方法是监听更改,打包并重新启动应用程序。还记得我们的client/router/pages.js文件,它是引入到客户端和服务器端的路由中的,所以服务器端和客户端的封装依赖都有pages.js,所以所有与pages组件相关的依赖都可以被客户端和服务器端监控。当一个组件被更新时,开发服务器帮助我们监控和更新客户端代码。现在我们必须自己处理以下如何更新和重启服务器代码。其实方法很简单,就是在服务器的包配置中开始监控,然后在插件配置中写一个重启的插件。插件代码如下:
webpack第一次运行后,插件会启动一个子进程,运行app.js,当文件发生变化时,会再次编译,判断是否有子进程。如果有子进程,那么重启子进程,从而实现自动重启。因为客户端和服务器是两种不同的打包服务和配置,所以当文件被修改时,它们将同时被重新编译。为了确保编译操作符合预期,需要确保先编译服务器,后编译客户端。因此,在客户端的手表配置中增加了一点延迟,如下图所示,默认为300毫秒,所以服务器在300毫秒后执行编译,而客户端在1000毫秒后执行编译。
现在重启问题已经解决了,但是我觉得还不够,因为在大部分的开发时间里,pages.js中的组件,也就是显示端的代码会频繁更新。如果总是重启编译后端代码,我觉得效率太低了。所以我觉得应该再优化一下。
拆开客户机/路由器/页面的包装,并单独包装
流程应该如下:添加一个webpack.server-dev-pages.js配置文件来分别监控和包dist/pages。服务器代码判断如果是开发环境,则每次在路由监控方法中执行dist/pages包时都会重新获取,在服务器监控配置中将忽略客户端文件夹。看起来有点混乱,但实际上,最终的效果是,当页面中的依赖组件被更新时,webpack.server-dev-pages.js被重新编译并打包到dist/pages中,服务器应用程序不被编译和重新启动,只需要从服务器应用程序路由中检索最新的dist/pages包,这确保了服务应用程序更新所有客户端组件,但服务器应用程序不会编译和重新启动。当服务器本身的代码被修改时,它将被编译并自动重启。所以最终,我们的开发环境需要启动3个打包的配置
网络包。服务器开发页面Webpack。服务器-devWebpack。客户端-设备服务器/路由器,如何清除和更新页面包
至此,一个满意的开发环境已经基本实现。后来觉得每次更新css都没必要重新打包后端页面。此外,css同构时不一致,只有警告,没有实际影响。因此,我忽略了服务器开发页面中较少的文件(因为我使用的较少)。这会导致一个问题,因为页面没有更新,所以当页面刷新时,会先显示旧的样式,然后同构完成,立即变成新的样式。这个时刻在开发环境中是可以接受的,不会影响任何事情。但是避免了不必要的编译。
你没做过的事
封装成封装性更强的三方脚手架css scope control webpack配置开发环境,图片路径会不一致。自己做站的初衷是自己学习使用,所以个人的东西太多了。我已经从自己的站里拉出来,删除了很多包和代码,只是为了让别人更快地理解核心代码。代码中有很多注释可以帮助别人理解。如果你想用现在的图书馆开发自己的站,绝对是可以的,可以帮助你更好的理解。如果用于商业项目,建议使用nextjs。Css不做范围控制,所以如果你想隔离范围,手动添加上层css隔离,比如。索引{.}包一层,或者自己尝试引入三方套餐。webpack的一般配置可以打包成一个文件,然后引入每个文件,然后单独修改。但是之前看其他代码的时候,发现这个方法会增加阅读的难度。另外配置内容不多,不封装更直观。在开发环境中,图像路径会不一致。例如,客户端地址请求地址是localhost.assets/xx.jpg,而服务器地址为assets/xx.jpg,可能会有警告,但不会影响。因为只有一条是绝对路径,另一条是相对路径。
最后的
ssr服务器渲染的实现是相当令人满意的,并且花费了大量的时间。感受装载速度。欢迎来到大诗人站,https://dashiren.cn/有些页面有界面要求,比如https://dashiren.cn/space,加载速度还是很快的。
仓库准备好了。下载试试吧。安装依赖项后,运行命令。https://github.com/zimv/react-ssr
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:实现ssr服务器渲染的方法和步骤是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。