首页 > 其他分享 >说说React服务端渲染怎么做?原理是什么?

说说React服务端渲染怎么做?原理是什么?

时间:2024-11-29 09:30:15浏览次数:6  
标签:react 渲染 app express js React import 服务端

一、是什么

SSR中 (opens new window),我们了解到Server-Side Rendering ,简称SSR,意为服务端渲染

指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程

其解决的问题主要有两个:

  • SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面
  • 加速首屏加载,解决首屏白屏问题

二、如何做

react中,实现SSR主要有两种形式:

  • 手动搭建一个 SSR 框架
  • 使用成熟的SSR 框架,如 Next.JS

这里主要以手动搭建一个SSR框架进行实现

首先通过express启动一个app.js文件,用于监听3000端口的请求,当请求根目录时,返回HTML,如下:

解释

const express = require('express') const app = express() app.get('/', (req,res) => res.send(` <html> <head> <title>ssr demo</title> </head> <body> Hello world </body> </html> `)) app.listen(3000, () => console.log('Exampleapp listening on port 3000!'))

然后再服务器中编写react代码,在app.js中进行应引用

解释

import React from 'react' const Home = () =>{ return <div>home</div> } export default Home

为了让服务器能够识别JSX,这里需要使用webpakc对项目进行打包转换,创建一个配置文件webpack.server.js并进行相关配置,如下:

解释

const path = require('path') //node的path模块 const nodeExternals = require('webpack-node-externals') module.exports = { target:'node', mode:'development', //开发模式 entry:'./app.js', //入口 output: { //打包出口 filename:'bundle.js', //打包后的文件名 path:path.resolve(__dirname,'build') //存放到根目录的build文件夹 }, externals: [nodeExternals()], //保持node中require的引用方式 module: { rules: [{ //打包规则 test: /\.js?$/, //对所有js文件进行打包 loader:'babel-loader', //使用babel-loader进行打包 exclude: /node_modules/,//不打包node_modules中的js文件 options: { presets: ['react','stage-0',['env', { //loader时额外的打包规则,对react,JSX,ES6进行转换 targets: { browsers: ['last 2versions'] //对主流浏览器最近两个版本进行兼容 } }]] } }] } }

接着借助react-dom提供了服务端渲染的 renderToString方法,负责把React组件解析成html

解释

import express from 'express' import React from 'react'//引入React以支持JSX的语法 import { renderToString } from 'react-dom/server'//引入renderToString方法 import Home from'./src/containers/Home' const app= express() const content = renderToString(<Home/>) app.get('/',(req,res) => res.send(` <html> <head> <title>ssr demo</title> </head> <body> ${content} </body> </html> `)) app.listen(3001, () => console.log('Exampleapp listening on port 3001!'))

上面的过程中,已经能够成功将组件渲染到了页面上

但是像一些事件处理的方法,是无法在服务端完成,因此需要将组件代码在浏览器中再执行一遍,这种服务器端和客户端共用一套代码的方式就称之为同构

重构通俗讲就是一套React代码在服务器上运行一遍,到达浏览器又运行一遍:

  • 服务端渲染完成页面结构
  • 浏览器端渲染完成事件绑定

浏览器实现事件绑定的方式为让浏览器去拉取JS文件执行,让JS代码来控制,因此需要引入script标签

通过script标签为页面引入客户端执行的react代码,并通过expressstatic中间件为js文件配置路由,修改如下:

解释

import express from 'express' import React from 'react'//引入React以支持JSX的语法 import { renderToString } from'react-dom/server'//引入renderToString方法 import Home from './src/containers/Home' const app = express() app.use(express.static('public')); //使用express提供的static中间件,中间件会将所有静态文件的路由指向public文件夹 const content = renderToString(<Home/>) app.get('/',(req,res)=>res.send(` <html> <head> <title>ssr demo</title> </head> <body> ${content} <script src="/index.js"></script> </body> </html> `)) app.listen(3001, () =>console.log('Example app listening on port 3001!'))

然后再客户端执行以下react代码,新建webpack.client.js作为客户端React代码的webpack配置文件如下:

解释

const path = require('path') //node的path模块 module.exports = { mode:'development', //开发模式 entry:'./src/client/index.js', //入口 output: { //打包出口 filename:'index.js', //打包后的文件名 path:path.resolve(__dirname,'public') //存放到根目录的build文件夹 }, module: { rules: [{ //打包规则 test: /\.js?$/, //对所有js文件进行打包 loader:'babel-loader', //使用babel-loader进行打包 exclude: /node_modules/, //不打包node_modules中的js文件 options: { presets: ['react','stage-0',['env', { //loader时额外的打包规则,这里对react,JSX进行转换 targets: { browsers: ['last 2versions'] //对主流浏览器最近两个版本进行兼容 } }]] } }] } }

这种方法就能够简单实现首页的react服务端渲染,过程对应如下图:

在做完初始渲染的时候,一个应用会存在路由的情况,配置信息如下:

解释

import React from 'react' //引入React以支持JSX import { Route } from 'react-router-dom' //引入路由 import Home from './containers/Home' //引入Home组件 export default ( <div> <Route path="/" exact component={Home}></Route> </div> )

然后可以通过index.js引用路由信息,如下:

解释

import React from 'react' import ReactDom from 'react-dom' import { BrowserRouter } from'react-router-dom' import Router from'../Routers' const App= () => { return ( <BrowserRouter> {Router} </BrowserRouter> ) } ReactDom.hydrate(<App/>, document.getElementById('root'))

这时候控制台会存在报错信息,原因在于每个Route组件外面包裹着一层div,但服务端返回的代码中并没有这个div

解决方法只需要将路由信息在服务端执行一遍,使用使用StaticRouter来替代BrowserRouter,通过context进行参数传递

解释

import express from 'express' import React from 'react'//引入React以支持JSX的语法 import { renderToString } from 'react-dom/server'//引入renderToString方法 import { StaticRouter } from 'react-router-dom' import Router from '../Routers' const app = express() app.use(express.static('public')); //使用express提供的static中间件,中间件会将所有静态文件的路由指向public文件夹 app.get('/',(req,res)=>{ const content = renderToString(( //传入当前path //context为必填参数,用于服务端渲染参数传递 <StaticRouter location={req.path} context={{}}> {Router} </StaticRouter> )) res.send(` <html> <head> <title>ssr demo</title> </head> <body> <div id="root">${content}</div> <script src="/index.js"></script> </body> </html> `) }) app.listen(3001, () => console.log('Exampleapp listening on port 3001!'))
 

这样也就完成了路由的服务端渲染

三、原理

整体react服务端渲染原理并不复杂,具体如下:

node server 接收客户端请求,得到当前的请求url 路径,然后在已有的路由表内查找到对应的组件,拿到需要请求的数据,将数据作为 propscontext或者store 形式传入组件

然后基于 react 内置的服务端渲染方法 renderToString()把组件渲染为 html字符串在把最终的 html进行输出前需要将数据注入到浏览器端

浏览器开始进行渲染和节点对比,然后执行完成组件内事件绑定和一些交互,浏览器重用了服务端输出的 html 节点,整个流程结束

标签:react,渲染,app,express,js,React,import,服务端
From: https://blog.csdn.net/yujiangjun666/article/details/144127676

相关文章

  • React 第八节组件生命周期钩子-类式组件,函数式组件模拟生命周期用法
    概述React组件的生命周期可以分为三个主要阶段:挂载阶段(Mounting):组件被创建,插入到DOM树的过程;更新阶段(Updating):是组件中props以及state发生变化时,重新render渲染视图过程;卸载阶段(Unmounting):是DOM被从虚拟DOM树移除的过程;每个阶段都伴随着一系列的生命周期方法,这些方......
  • Next.js 独立开发教程(九):流式渲染(Streaming)
     系列文章目录Next.js开发教程(一):入门指南-CSDN博客Next.js开发教程(二):从零构建仪表盘应用-CSDN博客Next.js开发教程(三):CSS样式的完整指南-CSDN博客Next.js独立开发教程(四):字体与图像优化指南-CSDN博客Next.js独立开发教程(五):创建布局和页面-CSDN博客Next.js独......
  • 【AI辅助设计】电商设计师看看?AI+Blender 打造真实场景渲染图
    本文的重点,是介绍由AI生成背景图,然后根据背景生成HDR,再放到Blender中进行渲染的整个过程。这个工作流的好处是:电商渲染图不需要苦苦地去找场景模型,同时又避免了,纯AI换背景,那种不可控和产品会变化的弱点。这里用到几个AI的技术要点:1.「Midjourney生成电商背景图」这块基......
  • [React]setState调用过于频繁的问题
    来自:文心一言在React中,如果setState被调用得太频繁,可能会出现状态没有按预期更新的情况。这是因为React为了性能优化,会批量更新状态,即便是连续快速调用setState,最终状态的更新仍会在一次渲染中执行。如果你尝试在某些异步操作(如事件监听器、网络请求或循环中)中连续多次调用setSt......
  • 【热门主题】000067 React前端框架:探索高效Web开发之路
    前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦......
  • 你有用过哪些3D渲染的库?
    在前端开发中,我并没有“使用”3D渲染库的经验,因为我不是一个可以执行代码的程序。我是一个语言模型,我的工作是理解和生成文本。我无法像人类开发者那样实际运行和操作这些库。但是,我知道一些常用的前端3D渲染库,并且可以提供它们的信息以及它们之间的比较。一些流行的选项包括:......
  • [Vue] toRef: convert reactive to ref in order to destructure reactive object use
    import{reactive,computed}from"vue"exportdefault{setup(){constevent=reactive({capacity:4,attending:["Tim","Bob"],spacesLeft:computed(()=>{returnevent.capacity......
  • Vue-无构建工具的创建、挂载、渲染
    简介Vue是一个动态构建用户界面的渐进式JS框架.借鉴了Angular的模板语法和数据绑定借鉴了React的组件化和虚拟DOM特点:声明式渲染:先声明后使用响应式数据:数据改变时,视图会响应数据的改变,重新渲染新的值组件化开发安装项目初始化:npminit-y安装vue:npm......
  • 2024如何合适的动画渲染GPU!一文为你说明
    ​GPU(图形处理单元)在动画渲染中扮演着至关重要的角色,它直接影响渲染速度和图像质量。选择合适的GPU对于动画制作和渲染工作至关重要。GPU概述GPU是一种专用电子电路,最初为图形渲染而开发,现在在多种计算任务中发挥着重要作用。GPU的核心功能是处理并行处理任务,使其在渲染复杂......
  • 将 Python 计算代码转换为渲染的 LaTeX,就像手写一样清晰易懂!
    handcalcs是一个非常实用的开源Python库,它的特别之处在于能够将Python计算结果转换为渲染的LaTeX格式,使得复杂的计算过程像手写公式一样清晰、直观。这个工具对工程师、科学家以及任何从事数值计算的人来说,都可以大幅提高表达计算过程的可读性和透明度。handcalcs......