Server side rendering in the long period of JSP, PHP, is already in use, but in the case of a single page application is popular, but still has a variety of solutions to support, because the service side rendering do have a lot of a lot of benefits, especially for the Node and three framework combining the front end of isomorphism, use a set of code, front and back side It combines the convenience of single-page application with the benefits of server-side rendering. Here is a look at the principle and process of React Server Render.
The React isomorphism
Key elements of React isomorphism
DOM consistency
Rendering the same Component on the front and back ends will output a consistent Dom structure.
Perfect Component properties and lifecycle and client render timing are key to React isomorphism.
React’s virtual DOM is stored in memory as a tree of objects and can be generated in any javascrT-BASED environment, so it can be generated in the browser and Node. This front and back isomorphism provides a prerequisite.
As shown above:
- The React virtual DOM can be generated in any Javascript supported environment, so it can be generated in the browser’s Node environment.
- The virtual DOM can be directly converted to a String.
- Then it is inserted into an HTML file and output to the browser.
The virtual Dom exists in the form of an object tree on both the front and back ends, but the way the prototype is displayed is different.
- In the browser, React uses ReactDom’s render method to render the virtual Dom to the real Dom tree to generate web pages
- However, there is no rendering engine in Node, so React provides two other methods:
ReactDOMServer.renderToString
和ReactDOMServer.renderToStaticMarkup
You can render it as an HTML string
Different life cycles
On the server, the Component lifecycle only goes to componentWillMount; the client is complete.
Client render timing
In isomorphism, the server renders the Component with the data as a complete HTML string and returns the data state to the client, which determines whether it can be used directly or needs to be remounted.
These are the basic conditions that React provides for isomorphic/server rendering. In the actual project application, we also need to consider other corner problems, such as the server side does not have window object, need to do different processing.
RenderToString and renderToStaticMarkup
ReactDOMServer provides methods for renderToString and renderToStaticMarkup, using renderToString in most cases, which adds a checksum to the component
React determines whether the client needs to rerender the same by checksum, then it does not rerender, omits the process of creating the DOM and mounting the DOM, then triggers events such as componentDidMount to deal with the pending matters on the server (event binding, etc.), Thus speeding up the interaction time; If not, the component will be remounted to Render on the client.
RenderToStaticMarkup does not generate react related data-*, and there is no checksum. The output HTML is as follows
The component is remounted on the client side, and client remount does not generate checknum(nor is it necessary), so this method is only used when the component rendered on the server is not needed by the client.
Checknum is actually the ADler32 algorithm value for the HTML fragment, which actually calls React.render(
- Take a look at
container
Is it null? If it is not null, it is considered that the result is straight. - And then whether the first element has
data-react-checksum
If the value is the same as that obtained by the browser’s AdLER32 algorithm and is compared to the data-checksum, if the value is the same as that obtained by the browser’s Adler32 algorithm, it is not required to render, otherwise it is re-rendered.
var MOD = 65521;
// This is a clean-room implementation of adler32 designed for detecting
// if markup is not what we expect it to be. It does not need to be
// cryptographically strong, only reasonably good at detecting if markup
// generated on the server is different than that on the client.
function adler32(data) {
var a = 1;
var b = 0;
for (var i = 0; i < data.length; i++) {
a = (a + data.charCodeAt(i)) % MOD;
b = (b + a) % MOD;
}
return a | (b << 16);
}
Copy the code
Matters needing attention
- Data status on the server is synchronized to the client
The data generated on the server needs to be returned along with the page, and the client uses this data to render to keep the state consistent. If renderToString is used on the server and the component is still remounted on the client, it is mostly because the HTML is not returned with renderToString on the server, or the format of the returned data is not correct, so you can pay attention to the prompt on Chrome during development
-
The server needs to pull the data in advance, and the client needs to call the platform difference on componentDidMount. The server rendering only executes on compnentWillMount, so to achieve isomism, you can write the pull logic to the React Class static method. On the one hand, the server can directly manipulate the static method to pull data ahead of time and then generate HTML from the data, on the other hand, the client can call the static method to pull data when componentDidMount
-
Keep the data deterministic here refers to the data that affects the result of the component render. For example, the following component will be rerendered by the client due to different random numbers generated on the component between the server and client.
Class Wrapper extends Component {
render() {
return (<h1>{Math.random()}</h1>); }};Copy the code
You can wrap math.random () into the Component props, generate a random number on the server, and pass it to the Component to ensure that the random number is consistent on both the client and server. Such as
Class Wrapper extends Component {
render() {
return (<h1>{this.props.randomNum}</h1>); }};Copy the code
RandomNum is passed to the server
let randomNum = Math.random()
var html = ReacDOMServer.renderToString(<Wrapper randomNum={randomNum} />);
Copy the code
- Platform to distinguish
When the current backend shares a set of code, such as the front-end specific window object, Ajax requests cannot be used in the back end. The back end needs to remove the front-end specific object logic or use a corresponding back end solution. For example, the back end can use HTTP. Therefore, it is necessary to distinguish platforms in the following ways
1. The code uses the front and back end common module, such as isomorphic-fetch 2. The front and back ends use Webpack to configure resolve.alias to correspond to different files. For example, the client uses /browser/request.js to make Ajax requests
resolve: {
alias: {
'request': path.join(pathConfig.src, '/browser/request'),}}Copy the code
Server webPack uses /server/request.js to replace HTTP
resolve: {
alias: {
'request': path.join(pathConfig.src, '/server/request'),}}Copy the code
3. Use webpack.definePlugin to add a platform-specific value at build time. This way, after compiling WebPack UglifyJsPlugin, code that is not on the current platform (unreachable code) will be removed without increasing the file size. For example, add the following configuration to webPack on the server side
new webpack.DefinePlugin({
"__ISOMORPHIC__": true
}),
Copy the code
Do judgment in JS logic
if(__ISOMORPHIC__){
// do server thing
} else {
// do browser thing
}
Copy the code
4. Windows are browser-specific objects, so they can also be used as platform differentiators
var isNode = typeof window= = ='undefined';
if (isNode) {
// do server thing
} else {
// do browser thing
}
Copy the code
componentWillReceiveProps
In, the method of relying on data changes should be considered incomponentDidMount
Be compatible with
For example, identity, which defaults to UNKOWN, is pulled from the background and its value is updated, triggering the setButton method
componentWillReceiveProps(nextProps) {
if (nextProps.role.get('identity') !== UNKOWN &&
nextProps.role.get('identity')! = =this.props.role.get('identity'))) {
this.setButton(); }}Copy the code
Isorphism, because the server has done the first data pull, so the above code in the client will never execute setButton method due to identity already exists, the solution can be done in componentDidMount compatibility processing
componentDidMount() {
/ /.. Determine whether it is isomorphism
if(identity ! == UNKOWN) {this.setButton(identity); }}Copy the code
reference
React direct implementation and principles
React isomorphism gives an optimization summary