手机版

React服务器渲染方案完美解决方案详解

时间:2021-08-28 来源:互联网 编辑:宝哥软件园 浏览:

最近在开发服务器端渲染工具,介绍了服务器端渲染,以及服务器端渲染的方式方法。在本文的最后,有两个关于服务器端渲染方法的想法。根据你的权衡,你会选择哪一个?

什么是服务器端渲染

使用“反应”来构建客户端应用程序。默认情况下,可以在浏览器中输出React组件来生成DOM和操作DOM。React也可以通过Node.js在服务器端转换成HTML,处理后的HTML字符串可以直接在浏览器端“渲染”。这个过程可以认为是“同构”,因为大部分应用程序代码都可以在服务器端和客户端运行。

为什么要使用服务器端渲染

与传统的spa(单页应用)相比,服务器端渲染(SSR)的优势主要在于:

更好的SEO,因为搜索引擎的爬虫爬行工具可以直接查看完全渲染的页面。更好的用户体验,对于网络条件较慢或者运行速度较慢的设备,加载后资源浏览器会直接渲染,服务器渲染的HTML在所有JavaScript下载执行完毕后才会显示。服务器端呈现的缺点

由于服务器和浏览器客户端环境的不同,有必要注意选择一些开源库,因为有些库无法在服务器上执行。例如,如果您有获取对象(如文档和窗口)的操作,您将在服务器上报告错误,因此您应该选择开源库。例如,使用服务器端呈现来设置专用于服务器端呈现的服务。与前一个相比,虽然客户端所需的静态资源不同,但也需要服务器端的知识和Node.js的运维部署,对于需要掌握的知识点,更多的服务器需要更多的负载,渲染在Node.js中完成,由于Node.js,会占用大量的CPU资源。下面描述服务器端呈现的一个“操作”。这个新操作出现了新的问题,比如API请求两次,各种服务器端的问题,你也无能为力。因为这个新工具是用Golang写的,你的团队或者你需要了解Golang,你说你生气了需要多学点。服务器渲染的两种方式

根据上面的介绍,我们知道了服务器端渲染的优缺点。我们可以根据利弊进行取舍。最近我们发现了各种各样的服务器端渲染解决方案,大致可以分为两类。

第一条路

传统的服务端渲染可以解决用户体验和更好的SEO。很多工具都使用这种方法,比如React (Next.js)和Vue (Nuxt.js)。

有些工具在服务器端的生产环境中运行webpack,实时编译,并缓存编译结果,这仍然是传统的方式,但是在服务器端运行webpack进行实时编译仍然是开发环境编译和预编译不好的问题。

我选择将webpack放在开发环境中,只做开发打包的功能,将客户端捆绑包、服务器捆绑包、资源映射文件assets.json、CSS等资源打包进行部署。

服务器捆绑用于服务器端渲染(SSR)客户端捆绑加载浏览器,浏览器通过捆绑加载更多其他模块(chunk)js资源映射文件assets.json。服务器捆绑包需要预插入那些模块(块)js和CSS,以改善用户体验。具体用法,我们可以看看我最近造的一个车轮kkt-ssr。这个轮子封装了工具的一部分。您只需要编写业务代码和少量服务器端呈现代码。它还附带了十几个例子,再加上一个相对完美的例子,react-router复赛,类似于next.js,但有相当大的区别。

第二条路

这是一个创新的方法,前端单页应用,过去怎么玩,现在怎么玩,多了一步就是你要先访问一个Rendora服务,在前面截取是否需要服务器端渲染。下图为官方图片:

这种方式原本只是一种想法。想法是前端不用处理服务器的渲染,所以是解决SEO。这些爬虫过来的时候,可以根据头部信息判断,写一个服务,然后把需要的内容给爬虫。昨天他们正好在GitHub的趋势榜单上,碰巧看到了Rendora工具,碰巧这么巧,刚好思路一致。这个工具主要是为web爬虫提供零配置的服务器端渲染,从而毫不费力地完善现代的Javascript框架(如React.js、Vue.js、Angular.js等)。)

这个方法很好。您不需要更改先前编写的项目的一个字,只需启动一个新的Rendora服务。对于来自前端服务器或外部(百度谷歌爬虫)的每一个请求,Rendora都会根据配置文件、头文件和路径进行检测或过滤,以确定Rendora应该只传递后端服务器返回的初始HTML,还是使用Chrome提供的无头服务器呈现的HTML。更具体地说,对于每个请求,有两条路径:

该请求被列入白名单作为SSR的候选(即过滤后的Get请求),Rendora将指示无头Chrome实例请求相应的页面,对其进行呈现,并返回包含最终服务器端的响应来呈现HTML。通常只有百度、谷歌、必应爬虫等网页抓取工具需要加入白名单。没有白名单(即请求不是GET请求或者没有通过任何过滤器),Rendora只会充当反向HTTP代理,只按原样传递请求和响应。Rendora可以看作是位于后端服务器之间的反向HTTP代理服务器(如Node.js/Express.js, Python/Django等)。),或者作为你的前端代理服务器(比如nginx、traefik、apache等。).

Rendora是我见过的近乎完美的动态渲染器,提供零配置的服务器端渲染

我们选择哪种服务器端渲染?

Rendora,新方法非常强大,有很多优点:

旧项目迁移方便,前后端代码不需要改动。性能可能更快,资源(CPU)消耗可能更少。Golang编写的很多二进制文件的缓存策略已经有了docker容器方案,服务器渲染的页面需要缓存。缓存引起的小问题有

通过缓存解决方案、性能问题和两次调用API的问题,服务器端渲染和客户端呈现渲染,API通常被调用一次,但现在被调用两次。

缓存页面无法及时清理。例如,如果网站发现用户发送了不良信息,需要清理,就需要清理缓存的页面。如果想要提升用户体验,浏览器端的一些页面需要服务器端进行渲染。此时服务器端需要请求API,会出现权限问题。或者,直接从缓存中读取的HTML可能会被服务器端和浏览器端不一致地呈现。

如果以上两种方法不在您的考虑范围内,那么Rendora将是您完美的服务器端渲染解决方案

摘要

感觉我的车轮kkt-ssr白写了。分析后发现还是有作用的,至少解决了一次不调用API和API调用权限导致渲染不一致的问题。但我推荐仁多拉的方式,这将是未来。

补充:

同构方案

这里,我们使用React技术系统进行同构。由于React本身的设计特点,以Virtual DOM的形式存储在内存中,这是在服务器端进行渲染的前提。

在客户端,通过调用ReactDOM.render方法将虚拟DOM转换为真实DOM,最后呈现到界面。

从“react-DOM”导入{render},从“app”导入。/app' render (app/

从“react-DOM/server”导入{rendertostring}“从导入应用程序”。/app' async函数(CTX){等待CTX。render ('index ',{ root : render tostring(app/)})}状态管理方案

我们选择Redux来管理反应组件的非私有组件状态,并配合社区中强大的中间件开发工具、Thunk、承诺等等来扩充应用。当进行服务端渲染时,创建商店实例后,还必须把初始状态回传给客户端,客户端拿到初始状态后把它作为预加载状态来创建商店实例,否则,客户端上生成的利润与服务端生成的利润不匹配,客户端将不得不再次加载数据,造成没必要的性能消耗。

服务端

从" react-DOM/服务器"导入{ renderToString },从“反应-还原”导入{提供者},从“redux”导入{ createStore },从“redux”导入应用程序./App "从导入rootReducer ”./reducers ' const store=Createstore(rootdreducer)异步函数(ctx) { await ctx.render('index ',{ root : renderToString(Provider store={ store } App//Provider),state : store。getstate()})} HTML

body div id='root'%- root %/div脚本窗口. REDUX _ STATE=%-JSON。stringify(STATE)%/脚本/正文客户端

从“反应世界”导入{ render },从“反应-还原”导入{提供者},从“redux”导入应用程序从导入{ createStore } ./App "从导入rootReducer ”./reduce RS ' const store=Createstore(rootdreduce,window .REDUX_STATE)呈现(提供程序存储={store} App//Provider,document.getElementById('root ')路由方案

客户端路由的好处就不必多说了,客户端可以不依赖服务端,根据混杂方式或者调用历史原料药,不同的统一资源定位器渲染不同的视图,实现无缝的页面切换,用户体验极佳。但服务端渲染不同的地方在于,在渲染之前,必须根据统一资源定位器正确找到相匹配的组件返回给客户端。

反应路由器为服务端渲染提供了两个API:

匹配在渲染之前根据统一资源定位器匹配路由组件-路由上下文以同步的方式渲染路由组件服务端

从" react-DOM/服务器"中导入{ renderToString },从“反应-还原”中导入{提供者},从“redux”中导入{ createStore },从“反应路由器”中导入{ match,RouterContext },从“反应路由器”中导入rootReducer ./reduce "从导入路由"。/routes ' const store=Createstore(rootdreduce)异步函数clientRoute(ctx,next){ let _ renderProps match({ routes,location: ctx.url},(错误,redirectLocation,renderProps)={ _ renderProps=renderProps })if(_ renderProps){等待ctx。呈现('索引',{ root 3: renderToString(提供程序存储={ store } RouterContext }._renderProps}//Provider),状态:存储。getstate()})else { wait next()} }客户端

从“反应路由器”导入{路由,索引路由},从“普通”导入。/常见的"从导入主页"。/Home "导入浏览自"。/浏览"从导入关于"。/About '常量路由=(路由路径='/'组件={公共}索引路由组件={Home} /路由路径='explore '组件={探索} /路线路径='关于'组件={关于}//路线)导出默认路由静态资源处理方案

在客户端中,我们使用了大量的ES6/7语法,jsx语法,css资源,图片资源,最终通过工具配合各种装货设备打包成一个文件最后运行在浏览器环境中。但是在服务端,不支持导入、jsx这种语法,并且无法识别对css、图像资源后缀的模块引用,那么要怎么处理这些静态资源呢?我们需要借助相关的工具、插件来使得Node.js解析器能够加载并执行这类代码,下面分别为开发环境和产品环境配置两套不同的解决方案。

开发环境

首先引入巴贝尔-波利希尔这个库来提供改革者运行时和核心js来模拟全功能ES6环境。

引入巴别塔-注册,这是一个需要钩子,会自动对需要命令所加载的射流研究…文件进行实时转码,需要注意的是,这个库只适用于开发环境。

引入钢性铸铁模块需要钩子,同样是钩子,只针对样式文件,由于我们采用的是半铸钢钢性铸铁(Cast Semi-Steel)模块方案,并且使用厚颜无耻来书写代码,所以需要节点-萨斯这个前置编译器来识别扩展名为。半导体色敏传感器的文件,当然你也可以采用较少的的方式,通过这个钩子,自动提取类名哈希字符注入到服务端的反应组件中。

引入资产需求挂钩,来识别图片资源,对小于8K的图片转换成base64字符串,大于8k的图片转换成路径引用。

//提供自定义再生器运行时和core-js require(' babel-poly ill ')//JavaScript必需的hook require(' babel-register ')({ preset s :[' es 2015 ',' react ',' stage-0']})//Css必需的hook require(' Css-modules-require-hook ')({扩展s 3360[).SCS '],预处理Css:(数据,文件名)=必需(' node-sass ').render sync({ 0数据,文件:文件名})。css,camelCase: true,生成escopedname : '[name]_ _[local]_ _[具有h :基数64:8]' })//Image required hook required(' asset-required-hook ')({扩展名s :[' jpg ',' png ',' gif ',' webp'],limit: 8000})产品环境

对于产品环境,我们的做法是使用工具分别对客户端和服务端代码进行打包。客户端代码打包这里不多说,对于服务端代码,需要指定运行环境为节点,并且提供polyfill,设置_ _文件名和__dirname为没错,由于是采用半铸钢钢性铸铁(Cast Semi-Steel)模块,服务端只需获取类名,而无需加载样式代码,所以要使用CSS-加载程序/局部变量替代钢性铸铁加载程序加载样式文件

//web pack。配置。js { target : ' node ',node: { __filename: true,__dirname: true },模块: { loaders :[{ test :/\ .js$/,exclude: /node_modules/,loader: 'babel ',query : {预设s :[' es 2015 ',' react ',' stage-0']},{ test: /\ .scss $/loaders :[' CSS/locals?modulescamelCaseimportLoaders=1 local identity name=[hash : base 64:8]',' sass' ] },{ test: /\ .(jpg|png|gif|webp)$/,loader: 'url?限制=8000' }] }}动态加载方案

对于大型网应用程序来说,将所有代码打包成一个文件不是一种优雅的做法,特别是对于单页面应用,用户有时候并不想得到其余路由模块的内容,加载全部模块内容,不仅增加用户等待时间,而且会增加服务器负荷网络包提供一个功能可以拆分模块,每一个模块称为大块,这个功能叫做代码拆分。你可以在你的代码库中定义分割点,调用要求。确保,实现按需加载,而对于服务端渲染,要求。确保是不存在的,因此需要判断运行环境,提供钩子函数。

重构后的路由模块为

//挂钩服务器如果(类型为要求。确认!==' function '){ require。execute=function(dependencies,callback){ callback(require)} } const routes={ child routes :[{ path : '/',component: require '。/common/containers/Root ' .默认情况下,indexroute : { getComponent(下一个状态,回调){ require。确认([],要求={回调(null,要求')./家庭/集装箱/应用程序' .default) }、“home”)} }、child routes :[{ path : ' explore '、getComponent(nextState,callback){ require。确认([],要求={回调(null,要求')./explore/containers/App’.default) }、' explore') } }、{ path: 'about '、getComponent(nextState,callback){ require。确认([],要求={回调(null,要求')./关于/容器/应用程序')。默认)}、"关于")} }]} }导出默认路由优化方案

vendor: ['react ',' react-dom ',' redux ',' react-redux']所有射流研究…模块以chunkhash方式命名

输出: {文件名: '[名称].[区块有:8]。js ',chunkFilename: 'chunk .[姓名]。[区块有:8]。js ',}提取公共模块,清单文件起过渡作用

新的网络包。优化。commonschunk插件({ name :[' vendor ',' manifest'],filename: '[name]).[区块有:8]。js'})提取钢性铸铁文件,以内容哈希方式命名

新的提取文本插件('[名称])。[contenthash :8]1 .css ')模块排序、去重、压缩

新的网络包。优化。occurrenceorderplugin(),//webpack2已移除新的网络包。优化。DedUPplugin(),//webpack2已移除新的网络包。优化。uglifyjsplugin({ compression : { warning : false },comments: false})使用巴别塔-插件-转换-运行时取代巴贝尔-波利希尔,可节省大量文件体积需要注意的是,你不能使用最新的内置实例方法,例如数组的包含方法

{ presets: ['es2015 ',' react ',' stage-0'],plugins :[' transform-runtime ']}最终打包结果

部署方案

pm2开始/服务器。js-I 0

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

版权声明:React服务器渲染方案完美解决方案详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。