基于React.js和Node.js的SSR实现方案详解
基本概念
SSR:服务器端渲染传统的服务器端渲染可以通过使用Java、php等开发语言来实现。随着Node.js及相关前端技术的不断进步,前端学生也可以在此基础上完成独立的服务器渲染。
流程:浏览器发送请求-服务器运行反应代码生成页面-服务器返回页面-浏览器下载HTML文档-页面准备就绪,即当前页面的内容由服务器生成并发送给浏览器。
对应的CSR:即客户端渲染过程:浏览器发送请求;服务器返回空白HTML(HTML包含一个根节点和js文件);浏览器下载js文件;浏览器运行反应代码;并且页面已经准备好了,也就是说,当前页面的内容由js呈现
如何区分页面是否呈现在服务器端:右键显示网页源代码。如果页面上的内容是HTML文档,则在服务器端呈现,否则在客户端呈现。
对比
CSR:第一屏渲染时间长,react代码在浏览器中运行,消耗浏览器的性能SSR。第一屏渲染时间短,react代码在服务器中运行,消耗了服务器的性能。为什么要用服务器来渲染?
优化了第一屏的加载时间,因为SSR直接返回生成的HTML,而普通CSR先返回空白HTML,然后浏览器动态加载JavaScript脚本,在页面有内容之前进行渲染;因此SSR的首屏加载更快,白屏时间减少,用户体验更好。
SEO(搜索引擎优化),搜索关键词时排名,对于大多数搜索引擎,只识别HTML内容,而不是JavaScript内容。(注意:原则上最好不要用服务器渲染,所以如果只有SEO要求,可以用预渲染技术代替)
构建服务器端呈现项目
(1)使用Node.js作为服务器和客户端之间的中间层,承担代理和处理cookie。
(2)使用水合物:当有服务器端渲染时,使用水合物代替渲染。其主要功能是将相关事件注入到HTML页面中(即让React组件的数据随HTML文档一起传输到浏览器页面中),使服务器端数据与浏览器端保持一致,避免屏幕闪烁,使首次加载体验更加高效流畅。
ReactDom.hydrate(App /,document . getelementbyid(' root '));(3)服务器代码的webpack编译:通常会创建一个webpack.server.js文件,除了常规的参数配置,还需要将目标参数设置为‘node’。
const Serverconfig={ target : ' node ',entry: '。/src/server/index.js ',output : { filename : ' bundle . js ',path : path . resolve(_ dirname ',/dist') },external s :[NodeExternals()],module : { rule s 3360[{ test :/\。js?$/,loader:' babel-loader ',exclude :[路径。join (_ _ dirname,'。/node _ modules')]}.]}(样式打包、代码压缩、运行环境配置等.).};(4)使用react-dom/server下的renderToString方法,将各种复杂的组件和代码在服务器上转换成HTML字符串返回给浏览器,并在初始请求时发送标签以加快页面加载,允许搜索引擎出于SEO目的抓取页面。
const render=(存储、路由、请求、上下文)={ const content=renderToString((提供程序存储={store} StaticRouter位置={req.path}上下文={context} div {renderRoutes(路由)}/div/static router/Provider));返回` html head title SSR/title/head body div id=' root ' $ { content }/div script src='/index . js '/script/body/html `;}app.get('* ',function (req,RES){ 0.const html=render(存储、路由、请求、上下文);RES . send(html);});类似renderToString的功能如下:i. renderToStaticMarkup:不同的是renderToStaticMarkup渲染的是纯HTML,没有数据反应,在JavaScript加载完成后,因为不知道服务器之前渲染了什么(页面可能会闪)。
二。renderToNodeStream:将反应元素渲染为其初始HTML,返回一个输出超文本标记语言字符串的可读流。
三。renderto statisticnodestream:与renderToNodeStream此类似,除了这不会创建反应在内部使用的额外数字正射影像图属性,例如数据重新传输。
(5) 使用回家的承担数据准备,状态维护的职责,通常搭配react-redux,redux-thunk(中间件:发异步请求用到行动)使用。(本猿目前使用比较多是就是Redux和Mobx,这里以Redux为例)。A.创建商店(服务器每次请求都要创建一次,客户端只创建一次):
const reducer=combinereeducers({ home : homeReducer,page1: page1Reducer,page 2: page 2 reducer });export const getStore=(req)={ return createStore(reductor,applyMiddleware(thunk。withextrault(serverAxios(req)));} export const getClientStore=()={ return CreateStore(reductor,window .STATE_FROM_SERVER,applyMiddleware(thunk。withextrale(client axios));b。行动:负责把数据从应用传到商店,是商店数据的唯一来源
export const getData=()={ return(dispatch,getState,axiosInstance)={ return axiosInstance。get('用户界面/XXX ').然后((RES)={派单({ type : ' HOME _ LIST ',LIST : RES . LIST })});}}C。减速器:接收旧的状态和行动,返回新的国家,响应行动并发送到商店。
导出默认值(状态={ list: [] },操作)={ switch(动作。type){ case ' HOME _ LIST ' : return }.状态,列表:操作。list }默认值:返回状态;} }导出默认值(状态={ list: [] },操作)={ switch(动作。type){ case ' HOME _ LIST ' : return }.状态,列表:操作。list }默认值:返回状态;}} D .使用反应还原的连接,提供商把组件和商店连接起来
供应者将之前创建的商店作为支柱传给供应者
const content=renderToString((提供程序存储={store}静态路由器位置={ req。path } context={ context } div { renderRoutes(路由)}/div/static router/Provider));连接([mapstatetrops],[mapdispatchtops],[mergeProps],[选项])接收四个参数常用的是前两个属性mapStateToProps函数允许我们将商店中的数据作为小道具绑定到组件上mapdispatchttoprops将行为作为小道具绑定到组件上
connect(mapstatetrops())(mapdispatchttoprops())(我的组件)(6)使用反应路由器承担路由职责服务端路由不同于客户端,它是无状态的做出反应提供了一个无状态的组件静态路由器,向静态路由器传递当前网址,调用ReactDOMServer.renderToString()就能匹配到路由视图。
服务端
从“react-router-dom”导入{静态路由器};从“反应-路由器-配置”导入{渲染路线}”从导入路由"。/router.js'StaticRouter位置={req.path}上下文={ { context } } { renderRoutes(routes)}/静态路由器浏览器端
从“react-router-dom”导入{浏览器错误};从“反应-路由器-配置”导入{渲染路线}”从导入路由"。/路由器。js ' BrowserRouter { renderRoutes(routes)}/BrowserRouter当浏览器的地址栏发生变化的时候,前端会去匹配路由视图,同时由于req.path发生变化,服务端匹配到路由视图,这样保持了前后端路由视图的一致,在页面刷新时,仍然可以正常显示当前视图。如果只有浏览器端路由,而且是采用浏览器外部,当页面地址发生变化后去刷新页面时,由于没有对应的HTML,会导致页面找不到,但是加了服务端路由后,刷新发生时服务端会返回一个完整的超文本标记语言给客户端,页面仍然正常显示。推荐使用反应-路由器-配置插件,然后如上代码在静态路由器和浏览器外部标签的子元素里加渲染路线(路线):建一个router.js文件
const routes=[{ component : Root,routes: [ { path: '/',exact: true,component: Home,loadData: Home.loadData },{ path: '/child/:id ',component: Child,loaddata : Child . loaddata routes 3:[path 3: '/Child/: id/grand-Child ',component当浏览器请求地址时,server.js可以在实际渲染前确定matchRouters要渲染的内容,调用loaderData函数调度动作,返回promise-promise all-render to string,最后生成HTML文档返回。
从“react-router-config”导入{ match routes } const loadbranch data=(位置)={ const branch=matchRoutes(路由,位置.路径名)const promises=branch . map(({ route,match })={ return route.loadData?路线。loaddata (match) : promise。resolve (null)})返回承诺。all (promises)} (7)编写组件时注意代码同构(即一组React代码在服务器端执行一次,在客户端再执行一次)。因为服务器端绑定事件无效,所以,服务器只返回页面样式(注水数据),同时也返回JavaScript文件。只有在浏览器中下载并执行JavaScript时,才能绑定事件。但是我们希望这个过程只需要写一次代码,这个时候使用同构,服务器在客户端执行的时候渲染样式,绑定事件。
优点:共享前端代码节省开发时间。缺点:由于服务器和浏览器环境的差异,会带来一些问题,比如找不到文档等对象,DOM计算报错,前端渲染和服务器渲染内容不一致;前端可以做非常复杂的请求合并和延迟处理,但为了同构,所有这些请求都要提前得到结果才能渲染。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:基于React.js和Node.js的SSR实现方案详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。