In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly explains "React server rendering and isomorphism how to achieve", the content of the explanation is simple and clear, easy to learn and understand, now please follow the editor's ideas slowly in depth, together to study and learn "how to achieve React server rendering and isomorphism"!
Background stage one
A long time ago, the development of a website or front-end and server-side maintenance in a project, may be using php+jquery.
At that time, the page rendering is placed on the server side, that is, when the user visits a page a, it will directly access the server route, and the server will render the page and return it to the browser.
In other words, all the content of the web page will be written in html and sent to the browser at one time.
At this point, you right-click to view the source code of the page, you can see all the code, or you go to view the html request, check "Preview", and you will find that it is a complete web page.
The second stage
But slowly people feel that the front-end collaboration in the above way is too troublesome and the coupling is too serious, which seriously affects the development efficiency and experience.
So with the emergence of vue/react, people began to get used to pure client-side rendering of spa.
At this time, the html will only write some main script files, there is no substantive content. Wait until html parses on the browser side, execute the js file, and then gradually create the element on the dom.
So when you look at the source code of the web page, you find that there is no content at all, only links to various scripts.
The third stage
Later, people slowly feel that pure spa is very unfriendly to SEO, and the white screen is very long.
For some active pages, what does a long blank screen mean? It means that the user simply does not have the patience to wait for the page to load.
So people want to go back to server rendering to improve the effect of SEO and shorten the white screen time as much as possible.
So do we have to go back to the development mode of the indignation of both people and gods? No, we now have a new solution, a new model, called isomorphism.
The so-called isomorphism means that different forms of the same structure and the same react code are executed at both ends.
Create a server-side rendering application
RenderToString
First of all, let's see what he is.
It can render a react element / component to the page and can only be used on the server side
So spa react-dom-> render corresponds to spa react-dom/server-> renderToString, the whole Hello World.
/ / MyServer.jsconst {renderToString} = require ('react-dom/server'); const React = require (' react'); const express = require ('express'); / / introduction of var app = express () by way of commonJS; const PORT = 3000 express' Const App = class extends React.PureComponent {render () {return React.createElement (' h _ 2'), function (req,res) {const content = renderToString (React.createElement (App)) / / render to HTML res.send (content); / / return result}) app.listen (PORT, () = > {console.log (`server is listening on ${PORT} `);})
After the server is started, the manual web page accesses the corresponding port locally
As you can see, what is returned is hello world, which is a server application!
Webpack configuration
After the application is written, the webpack configuration on the browser side is required.
Const path = require ('path'); const nodeExternals = require (' webpack-node-externals'); / / No packaging when packing node_modulesconst CopyWebpackPlugin = require ('copy-webpack-plugin') Module.exports = {entry: {index:path.resolve (_ _ dirname,'../server.js')}. Mode:'development', target:'node',// does not include packets such as path and fs that come with node. It must be related to node devtool: 'cheap-module-eval-source-map',//source-map configuration. This piece can be understood to provide faster packaging performance output: {filename:' [name] .js', path:path.resolve (_ _ dirname,'../dist/server') / / common output path}, externals: [nodeExternals ()], / / do not enter packets in node_modules resolve: {alias: {'@': path.resolve (_ _ dirname,'../src')} Extensions: ['.js']}, module: {/ / babel conversion configuration rules: [{test:/\ .js $/, use:'babel-loader', exclude:/node_modules/}]}, plugins: [/ / public directory that can be found in general applications Directly copy to new CopyWebpackPlugin ([{from:path.resolve (_ _ dirname,'../public'), to:path.resolve (_ _ dirname,'../dist')}])]} under the dist directory
Cli is used to use, writing configuration is a bit torture, how to use it after writing it? Package.json configuration run script:
"scripts": {"build:server": "webpack--config build/webpack-server.config.js-- watch", "server": "nodemon dist/server/index.js"}
So, pack a bag first.
As you can see, there are a lot of things that I can't understand.
At this time, it can be run.
Up to now, so many configurations have been written, in fact, it is only for the server to support the basic running configuration / environment on the browser side.
Bind a click event to the H2 tag
Import React from 'react';import {renderToString} from' react-dom/server';const express = require ('express'); const app = express (); const App = class extends React.PureComponent {handleClick= (e) = > {alert (e.target [XSS _ clean]);} render () {return Hello Worldlings;}}; app.get (' /', function (req,res) {const content = renderToString (); console.log (content); res.send (content);}) App.listen (3000)
If you go for a run at this time, you will find that when you click, there is no response!
Think about it for a moment. RenderToString just converts elements into strings, and events are not bound at all.
This is when the isomorphism comes!
Then the isomorphism is:
The same code, running through the server, generates a html.
If you run the same code on the client, you can respond to various user actions.
So we need to extract the App separately.
Src/app.js
Import React from 'react';class App extends React.PureComponent {handleClick= (e) = > {alert (e.target [XSS _ clean]);} render () {return Hello wordings;}}; export default App
Src/index.js
It is written in the same way as a normal spa application
Import React from 'react';import {render} from' react-dom';import App from'. / app';render (, document.getElementById ("root"))
Build/webpack-client.config.js
Deal with the packaging logic of the client code
Const path = require ('path') Module.exports = {entry: {index:path.resolve (_ _ dirname,'../src/index.js') / / path modification}, mode:'development', / * target:'node', client does not need this configuration * / devtool: 'cheap-module-eval-source-map', output: {filename:' [name] .js', path:path.resolve (_ _ dirname,'../dist/client') / / path modification} Resolve: {alias: {'@': path.resolve (_ _ dirname,'../src')}, extensions: ['.js']}, module: {rules: [{test:/\ .js $/, use:'babel-loader', exclude:/node_modules/}]}}
Run the script and add it to him.
"build:client": "webpack--config build/webpack-client.config.js-watch"
Run it.
Npm run build:client
Server references packaged client resources
Import express from 'express';import React from' react';import {renderToString} from 'react-dom/server';import App from'. / src/app';const app = express (); app.use (express.static ("dist")) app.get ('/', function (req,res) {const content = renderToString () Res.send (`root ${content} `); / / manually create the root node to bring in the App tag content}); app.listen (3000)
To test again, it is found that the page rendering is not a problem, and can also respond to user actions, such as click events.
Hydrate
After the above five steps, it looks fine, but our console will output some warnning
Warning: render (): Calling ReactDOM.render () to hydrate server-rendered markup will stop working in React v18. Replace the ReactDOM.render () call with ReactDOM.hydrate () if you want React to attach to the server HTML.
The difference between ReactDOM.hydrate () and ReactDOM.render () is:
ReactDOM.render () clears out all the children of the mounted dom node, and then regenerates them.
ReactDOM.hydrate () reuses the children of the mounted dom node and associates it with the virtualDom of the react.
In other words, ReactDOM.render () redoes all the work done by the server, while ReactDOM.hydrate () carries out in-depth operations on the basis of the work done by the server.
So let's modify the client's entry file src/index.js to change render to hydrate
Import React from 'react';import {hydrate} from' react-dom';import App from'. / app';hydrate (, document.getElementById ("root")); isomorphic process summary
The server generates html according to React code
The client initiates a request, receives the html from the server, and parses and displays it.
The client loads resource files such as js
The client executes the js file to complete the hydrate operation
The client takes over the overall application
Routin
When rendering on the client side, React provides BrowserRouter and HashRouter for us to handle routing, but they both rely on window objects and there is no window on the server side.
But react-router provides StaticRouter for our server rendering service.
Next, we simulate to add a few pages to achieve the function of routing.
Construct two pages: Login and User
/ / src/pages/login/index.jsimport React from 'react';export default class Login extends React.PureComponent {render () {return login}} / / src/pages/user/index.jsimport React from' react';export default class User extends React.PureComponent {render () {return user}}
Add server-side routing
/ server.jsimport express from 'express';import React from' react';import {renderToString} from 'react-dom/server';import {StaticRouter,Route} from' react-router';// server uses static routes import Login from'@ / pages/login';import User from'@ / pages/user';const app = express (); app.use (express.static ("dist") app.get ('*', function (req,res) {const content = renderToString ()) Res.send (`ssr ${content} `);}); app.listen (3000)
At this time, you will find a phenomenon, when you modify the route to Login through url on the page, the word login on the interface flashes away. Why?
Because although the server-side routing is configured, and the module is indeed embedded, but! The client hasn't processed it yet.
Add client rout
/ / src/index.jsimport React from 'react';import {hydrate} from' react-dom';import App from'. / app';import {BrowserRouter as Router, Route} from 'react-router-dom';import User from'. / pages/user';import Login from'. / pages/login';hydrate (, document.getElementById ("root")
Visit / user and / login respectively and find that it is ready to render normally, but! It is obviously the same mapping rule, but the root component of the route is different, and it is too painful to write it twice, so there is the next route isomorphism.
Route isomorphism
Is there any way to write the route once not only on the client side, but also on the server side? Like app.js?
So let's first look at the similarities and differences between the two ends of the route:
What they have in common: the mapping of paths and components is the same
Difference: the components referenced by the route are different, or implemented in a different way
The relationship between paths and components can be clearly described in abstract language, which is what we call routing configuration.
Finally, we provide a converter that can be converted to server or client routing according to our needs.
/ / New src/pages/notFound/index.jsimport React from 'react';export default () = > 404
Routing Profil
/ / src/router/routeConfig.jsimport Login from'@ / pages/login';import User from'@ / pages/user';import NotFound from'@ / pages/notFound' Export default [when {type:'redirect',// triggers redirection, unified return to user exact:true, from:'/', to:'/user'}, {type:'route', path:'/user', exact:true, component:User}, {type:'route', path:'/login', exact:true, component:Login}, {type:'route', path:'*', component:NotFound}]
Router converter
Import React from 'react';import {createBrowserHistory} from "history"; import {Route,Router,StaticRouter,Redirect,Switch} from' react-router';import routeConfig from'. / routeConfig';const routes = routeConfig.map ((conf,index) = > {/ / route distribution, traversing routes, judging that type follows the corresponding logical const {type,...otherConf} = conf; if (type==='redirect') {return;} else if (type===' route') {return;}}) Export const createRouter = (type) = > (params) = > {/ / distinguish server/client because it is created differently / / params is used to handle the redirection problem if (type==='client') {const history = createBrowserHistory (); return {routes}} else if (type==='server') {/ / const {location} = params; return {routes}
Client entry
/ / src/index.jsimport React from 'react';import {hydrate} from' react-dom';import App from'. / app';hydrate (, document.getElementById ("root"))
Client app.js
/ / src/app.jsimport React from 'react';import {createRouter} from'. / router'class App extends React.PureComponent {render () {return createRouter ('client') ();}}; export default App
Server entrance
/ / server.jsimport express from 'express';import React from' react';import {renderToString} from 'react-dom/server';import {createRouter} from'. / src/router'const app = express (); app.use (express.static ("dist")) app.get ('*', function (req,res) {const content = renderToString (createRouter ('server') ({location:req.url})) Res.send (`ssr ${content} `);}); app.listen (3000)
Redirection problem
When we redirect to / user here, we can see that the content returned by html is different from the content that renders the page.
This means that the redirect operation is done by the client, and what we expect is to access the index.html request first, return 302, and then a new user.html request appears.
Https://v5.reactrouter.com/web/api/StaticRouter react provides a way to handle redirection
Import express from 'express';import React from' react';import {renderToString} from 'react-dom/server';import {createRouter} from'. / src/router'const app = express (); app.use (express.static ("dist")) app.get ('*', function (req,res) {const context = {}; const content = renderToString (createRouter ('server') ({location:req.url, context})) / / when Redirect is used, context.url will contain the redirected address if (context.url) {/ / 302res.redirect (context.url);} else {res.send (`ssr ${content} `);}}); app.listen (3000)
At this time, we will test it again, and we will find that it meets the expectations. There are two requests, one 302 and one user.html.
404 problem
We randomly enter a route that does not exist and find that the content returns 404 as scheduled, but the request is indeed 200, which is wrong.
/ / server.jsimport express from 'express';import React from' react';import {renderToString} from 'react-dom/server';import {createRouter} from'. / src/router'const app = express (); app.use (express.static ("dist")) app.get ('*', function (req,res) {const context = {}; const content = renderToString (createRouter ('server') ({location:req.url, context})) / / when Redirect is used, context.url will contain the redirected address if (context.url) {/ / 302 res.redirect (context.url);} else {if (context.NOT_FOUND) res.status (404) / / determine whether to set the status code to 404 res.send (`ssr ${content} `);}}); app.listen (3000)
RouteConfig.js
/ / routeConfig.jsimport React from 'react';// before component:NotFound// modification render: ({staticContext}) = > {/ / receive and judge attributes, decide whether to render 404 page if (staticContext) staticContext.NOT_FOUND = true Return} Thank you for your reading. The above is the content of "how to achieve rendering and isomorphism on React server". After the study of this article, I believe you have a deeper understanding of how to achieve rendering and isomorphism on React server, and the specific usage needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.