The seventh episode of the Aiti Tribe live class: How to build isomorphic applications using React

The seventh episode of the Aiti Tribe live class: How to build isomorphic applications using React

[51CTO.com original article] With the development of the front-end, for the sake of user experience, H5 increasingly uses the SPA architecture, resulting in more and more JS codes and a large size. At this time, the traditional ajax method becomes slow when accessing the first screen, and ajax has a natural disadvantage in SEO. At this time, server-side rendering is back. We use React with React Router and other libraries to implement server-side rendering, making the first screen faster and SEO better. So, how to use React to build isomorphic applications? We specially invited Chen Guoxing, the front-end architect of B&Q, to share live.

With the development of the front-end, for the sake of user experience, H5 increasingly uses SPA architecture, resulting in more and more JS code and a large size. At this time, the traditional ajax method becomes slow when accessing the first screen, and ajax has a natural disadvantage in SEO. At this time, server-side rendering is back. We use React with React Router and other libraries to implement server-side rendering, making the first screen faster and SEO better. So, how to use React to build isomorphic applications? We specially invited Chen Guoxing, the front-end architect of B&Q, to share live.

Introduction

1. Why use SPA on mobile devices

2. Comparison of loading speed between traditional ajax method and server-side rendering

3. Detailed explanation of server-side rendering technology

4. Some things to note when writing isomorphic react code

We will use libraries such as react, react-router, and redux. The code examples are from previous projects. react-router is version 2, and there may be some differences with the latest API.

1. Why use SPA on mobile devices?

Let's start with why we use SPA. This is because of the development of mobile Internet. If the page jump is done by traditional link jump, especially in the 2.5G and 3G era, the network speed is slow and unstable. It is easy to click on the link and then see a blank page. If you are lucky, you will see a new page after a while. If you are unlucky, you will stay on the blank page. Therefore, SPA is needed, at least when the network is not good, you can still see the page, so that the user experience will be better.

Because the SPA development method inevitably leads to the client-side JS being rich client-side JS, which brings up a problem: how to manage the large amount of code and how to maintain it. This is why early MVC frameworks such as BackBone and SpineJs, as well as later frameworks such as MVP and MVVM, gradually brought the original server-side architecture ideas to the front end. Currently, angular, vue, and react are the most popular.

Some people may ask, why not choose Angular or Vue? To put it in a popular saying: Angular (Vue) you are a good guy, but we are not suitable. Of course, the real reason is that React's component-based thinking just matches what I want, and it is an agreement on technical thinking. When React came out, Vue had not yet come out, and Angular was really heavy and complicated.

2. Traditional Ajax Problems and Server-side Rendering Loading Speed ​​Comparison

Today we are talking about isomorphism, which starts with server-side rendering (SSR), also known as first-screen optimization. Let me steal a picture to look at the traditional page rendering process.

The earliest web development method was actually server-side rendering, but later people felt that the experience was not good and they had to refresh the page every time, so ajax was born. Initially, ajax had no problem. However, the mobile era came, and JS frameworks came. JS became more and more popular.

As can be seen from the above figure, when we want to access a page, we first render a blank page with no data, and then load resources, such as CSS, JS. A packaged and compressed JS file may even be several hundred KB. After JS is loaded, the API request is initiated. The user has to continue waiting until the request comes back before they can see a real page. So at this time, it is slower, and the server-side rendering method comes back.

I'll post a picture showing the access situation in an extreme case with slow 3G speed.

On slow 3G, without calling the interface, the total time until normal access is possible is 22.94s (excluding image loading). As you can see, the login page and main.css are loaded in 3.96s.

If server-side rendering is used, the page can be viewed without js, that is, the time required for the login page and css to load is the time required for the actual page to be viewed. If the traditional ajax method is used, the time required is more than 22 seconds, which is about 6 times the difference. If the interface call is added, we have tested that the time required for users to view the first screen is about 8-10 times the difference.

The first screen time of server-side rendering is: page+api request+css, and the page already contains data.
The first screen time of the client is: page+css+js+api request.

In addition to the client needing to load a large js file, API requests are generally faster on the server side. Here is a brief explanation of the concept of the home screen: the page you enter from anywhere is the home screen. In other words, to do isomorphic, first of all, we need to ensure that there is no JS, and that it can be accessed directly from any address in the browser. This is the same as the early server-side rendering. Therefore, this is also the reason why it can be more SEO-friendly.

3. Detailed explanation of server-side rendering technology

Why do we use the isomorphic approach of reusing code between the client and the server? Maintainability issues. The client is not secure, so the server cannot trust the client and needs to do various checks, including UI rendering after pulling data. This requires writing the same logic code for both the front-end and back-end. Reuse is necessary for development efficiency and maintainability.

In this regard, nodejs has a natural advantage. If isomorphism is not considered, server-side rendering is actually very simple. React provides a method: renderToString(). Just put the data it obtains into the template file, such as the ejs file of nodejs. For code reuse, we will consider putting the UI on the server for rendering, the logic on the server, and the API request code is also shared. It is best to write the route only once. React router supports server-side routing, and it also provides some friendly APIs for server-side rendering, such as Link.

Next, let's talk about the specific code. First the route definition.

Here, the history attribute is different on the browser side and the server side, so it needs to be passed in. The browser side uses browserHistory:

  1. import { browserHistory } from 'react-router'  

The server uses createMemoryHistory:

  1. import { RouterContext, createMemoryHistory, match } from 'react-router'

We paste all the routing configurations on the server side (nodejs), which actually uses the method provided by react-router.

  1. server.get('*', (req, res, next) => {
  2. const history = createMemoryHistory()
  3. const routes = createRoutes(history)
  4. let store = configStore()
  5. match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
  6. if (err) {
  7. res.status(500).send(err.message)
  8. } else if (!renderProps) {
  9. res.status(404).send('page not found')
  10. } else {
  11. getComponentFetch(renderProps, history, store).then(() => {
  12. let reduxState = escape(JSON.stringify(store.getState()))
  13. let html = ReactDOM.renderToString(
  14. <Provider store={store}>
  15. {<RouterContext {...renderProps} />}
  16. </Provider>
  17. )
  18. res.render('home', { html, scriptSrcs, cssSrc, reduxState })
  19. })
  20. .catch((err) => {
  21. next(err)
  22. })
  23. }
  24. })
  25. })
  26. function getComponentFetch (renderProps, history, store) {
  27. let { query, params } = renderProps
  28. let component = renderProps.components[renderProps.components.length - 1].WrappedComponent
  29. let promise = component && component.fetchData ? component.fetchData({ query, params, store, history }) : Promise.resolve()
  30. return promise
  31. }

The route matches all requests. When accessed, the corresponding react component is obtained according to the route configuration. Because the API interface needs to be called immediately on the server to obtain data, we will put a static method in the container component: fetchData, call this method to obtain data, and then put it in a variable and pass it to the ejs template file. Of course, our page has already rendered the data at this time. The data of this reduxState variable is used for rendering after js is loaded.

Let's take a look at the client code:

  1. let reduxState = {}
  2. if (window.__STATE__) {
  3. try {
  4. reduxState = JSON.parse(unescape(__STATE__))
  5. } catch (e) {
  6. }
  7. }
  8. const store = configStore(reduxState)
  9. ReactDOM.render((
  10. <Provider store={store}>
  11. {createRoutes(browserHistory)}
  12. </Provider>
  13. ), document.getElementById('container-root'))

window.__STATE__ This is the value of the variable reduxState that I passed from the server to initialize the redux store.

At the same time, in order to avoid the server requesting data once for the first screen and the browser requesting data again, we can pass the displayName of the current container component from the server back to the browser. In this way, if there is a value in the component, no fetch request will be initiated, but the value of the redux store will be used directly.

I'll also post the approximate code for fetchData:

  1. static fetchData ({store}) {
  2. let cityId = global.currentCityId
  3. return store.dispatch(actions.getHomeData(cityId))
  4. }

The purpose of writing this method is to reuse the logic of redux, whether it is action or store. In this way, we don't need to master a lot of nodejs knowledge, we only need to configure the routing on the server side to achieve the reuse of a set of code on the nodejs and browser side. Including UI, logic, redux, routing. In the future, we only need to write components, data requests, logic, etc.

4. Some things to note when writing isomorphic react code

Finally, let me talk about some points.

1. In the initial rendering cycle of react (constructor\componentWillMount\render), do not write code for browser-related objects, such as window. In addition: please note that componentDidMount is executed on the browser side, not on the node side. Also, do not write setState in the above life cycles.

2. After the user's first screen is rendered, it is possible to perform operations immediately without loading js, such as link jump or form submission, so it is necessary to assume that normal access is possible without js. For example, form submission uses form, and links use href (link of react router) instead of onClick. Here, the Link of react router will automatically convert the link into hash form after your js is loaded. At the same time, after js is loaded, the form event or link can be transferred to js for processing, and all subsequent pages will jump in ajax mode. To supplement the rendering process of the isomorphic method: user initiates a request->server receives the request->matches the route->pull data->renders the interface->pull JS code->matches the browser route->walks the life cycle of the component corresponding to the route->pull data->updates the component. Therefore, when all js are downloaded, your onClick event can really take effect.

3. The browser needs to access the API address, which involves multiple environments. For convenience, I use my node as a proxy to transfer API requests. In this way, the API address requested by the browser can be http://localhost. The nodejs side uses different API interface configurations according to different environments, and this has the additional benefit of bypassing cross-domain. The API backend service does not need to configure cross-domain, which is so troublesome, and the browser request can also have one less option to verify whether cross-domain access is allowed.

React isomorphic, that's pretty much it.

The following questions are from the 51CTO developer community friends and sharing

Q: Java-workman-Beijing: If I only use react+ajax, will the efficiency change? It is not a new application, but I just use react's dom to display it on the original basis. Will it be much different from ordinary ajax?

A: B&Q - Mr. Chen: The efficiency is what I said before. If you want the data to come out, you have to wait for your JS file to be downloaded and then initiate a request, so it will definitely be slower.


Q: Front-end-Jouryjc-Shenzhen: Teacher, could you please post the project github.

A: Mr. Chen from B&Q: I made a startkit myself, but I didn’t upload it to GitHub.


Q: Data-unicorn-Beijing: Is ant.design the best react framework at present?

A: Mr. Chen from B&Q: Ant.design is not a react framework. It is just a UI.


Q: Front-end - Sister Qiuxiang - Shenzhen: How is node used as a proxy to transfer API requests? Is this http-proxy done on the server side or on the client side?

A: B&Q-Teacher Chen: Use http-proxy. On the nodejs side.

  1. import httpProxy from 'http-proxy'  
  2. const proxy = httpProxy.createProxyServer({
  3. target: `${targetUrl}/api`
  4. })
  5. server.use( '/api' , (req, res) => {
  6. proxy.web(req, res)
  7. })

Q: Front-end - Sister Qiuxiang - Shenzhen: When and how is the static fetchData method called?

A: B&Q-Mr. Chen:


Q: Data-unicorn-Beijing: Which react UI framework do you recommend?

A: Mr. Chen from B&Q: This depends on the specific scenario. We generally do not use UI frameworks, but design based on specific scenarios. For the backend, you can consider using Ant.Design, but I heard that it is relatively large and not suitable for end users.


Q: Front-end - Sister Qiuxiang - Shenzhen: Teacher Chen, do we need a node server to do this server-side rendering?

A: B&Q - Teacher Chen: That’s right.


Q: Front-end - Sister Qiuxiang - Shenzhen: How do we build and configure this node server?

A: Mr. Chen from B&Q: Generally, it is best to use node, because the language is the same and the reusability is the highest. I use express, and there are only a few lines of code, which are basically all pasted. It is actually very simple. Just configure a route and a static method for obtaining data for the nodejs end to call. For the rest, just pay attention to some details.


Q: Front-end - Sister Qiuxiang - Shenzhen: By the way, when we do this react isomorphism, do we need the operation and maintenance staff to help us with any configuration? Is it the same as the server without react isomorphism before?

A: B&Q-Mr. Chen: You need to run a nodejs service. Maybe your previous page was rendered by Java, and now you can just hand it over to nodejs. Java only needs to provide an API interface.


Q: Daiwan-Banbun-Wugui: "Java and the like only need to provide an API interface." Does this mean that the front-end needs to set up a node server itself?

A: B&Q-Mr. Chen: NodeJS server. Isomorphism means that the server and client reuse a set of codes. So since there is a server.


Q: Java-workman-Beijing: Mr. Chen, can you briefly describe the essence or the most beautiful part of React?

A: Mr. Chen from B&Q: What I admire most is that React has such complex functions, but its exposed API is very simple. It can be said that as long as you have a render method, you can get started. If you understand props and state, you can write most functions. The ability to simplify is very profound.

[51CTO original article, please indicate the original author and source as 51CTO.com when reprinting on partner sites]

<<:  How do I implement offline caching of web pages step by step?

>>:  Why I still prefer Eclipse to IntelliJ IDEA

Recommend

Japanese TV makers retreat to their home market

Recent earnings reports from Sharp to Panasonic s...

How to choose the first ice cream in summer?

Image from: freepik.com Summer is getting closer ...

Dressing and Matching Training Camp

Dressing and Matching Training Camp Quick Match T...

Nanny-level tutorial on Tik Tok marketing!

Today I bring you a nanny-level teaching course o...

2021 Clothing Short Video and Live Streaming Marketing Report

As one of the earliest categories to enter Douyin...

Detailed introduction to Shenma search promotion account operation

1. What is the structure of a Shenma promotion ac...

Galaxy S4 spontaneously catches fire again: battery not original

According to foreign media reports, a Samsung Gala...

Mobile Internet Channel Promotion Methodology (with case studies)

Friends often ask me that they spent a lot of mon...