抹桥的博客
Language
Home
Archive
About
GitHub
Language
主题色
250
952 words
5 minutes
React-based Frontend Project Development Summary

Technology Stack#

Our project primarily uses the following technologies for development, complemented by some other auxiliary tools.

  • react
  • react-router
  • redux
  • react-redux

Development and Production Environments#

Development Environment#

Since the project employs a frontend-backend separation, we require a complete development environment that includes the following functionalities:

  • Data Mocking
  • Webpack live compilation and hot reloading
  • Convenient frontend-backend integration

Based on these requirements, we built this complete development environment using Express, Webpack, and Webpack-dev-middleware.

Development Environment

As you can see, all browser requests are intercepted by the local Node.js service. Static resource requests are delegated to webpack-dev-middleware for processing, while API requests are handled differently depending on the environment.

Local Development#

When ENV = 'development', which is the development environment, the page is rendered directly by reading local mock data.

Frontend-Backend Integration#

When ENV = 'api', which we consider the integration environment, API requests are forwarded by Node.js to the actual backend service address for integration, thereby avoiding cross-origin issues that would arise from direct calls.

This allows for direct integration of local development code with the backend, significantly improving efficiency and eliminating the need for repeated build and deployment steps to the server.

Production Environment#

The frontend and backend are deployed separately, with all static resources hosted on a CDN (example.cdn.com).

This means our page is at example.cdn/index.html, but the API requests are to example.163.com/api/xxx. We certainly cannot have users directly access example.cdn.com/index.html, as this is unreasonable and introduces cross-origin issues.

So, when accessing example.dai.163.com, how do we retrieve our HTML page? See the diagram below:

Production Environment

An Nginx server is set up between the client and the backend service. When we access example.dai.163.com, there are two types of requests:

  • HTML page resources
  • API requests

Both types of requests first pass through Nginx. Here, a determination is made: if it’s a page request, Nginx forwards it to the CDN; otherwise, it’s handed over to the backend service to respond to the API request.

Once the page is retrieved, all other static resources like CSS and JS are directly requested from the CDN. There’s nothing more to say about that.

Data Flow#

Leveraging Redux for data flow management, let’s look at this diagram.

Data Flow

First, the store is generated via middleware and reducer, then the project’s initial state is obtained, and this initial state is used to render the page’s initial status.

Taking the Home page as an example, Home first receives the initial state as a prop via the connect method provided by react-redux. The Home component is composed of multiple different child components, and the data required by these components is then passed from Home to its child components via props.

Once the initial state of Home has loaded, we need to request some user data from the backend. At this point, we dispatch an action in the following format:

{
  types: ['home/start','home/success','home/failure'],
  payload: {
    api:
    ...
  },
  meta: {
    isApi: true
  }
}

All actions will pass through each middleware in the sequence we define.

Here, our action will be hit by callApiMiddleware via the isApi flag in meta, and it will perform the corresponding operations.

For instance, within this middleware, we make actual API requests, dispatch corresponding actions upon request success or failure, and handle some unified business logic. For example, we have a consistent convention for the code value returned by backend APIs: assuming 1 for success, 2 for failure, and 3 for not logged in. We can then process these business logics within the middleware.

After the request succeeds and the page is rendered, suppose a user clicks a button that needs to invoke some native functionality, such as taking a photo. We then dispatch a camera/start action to invoke the camera functionality:

{
  types: ['sdk/start','sdk/success','sdk/failure'],
  payload: {
    command:
    ...
  },
  meta: {
    isSDK: true
  }
}

Similarly, this action will be recognized and processed by EpaySDKMiddleware. When invoking native functionality, to ensure security, we need to send a request to the backend to obtain a signature. At this point, EpaySDKMiddleware can dispatch an API request action, which will also go through all middlewares. This API request action will then be processed by callApiMiddleware just like the flow described above.

The existence of middleware makes the entire process very clear: API request middleware only handles API requests, and native API call middleware only handles native calls. When a native API call requires a backend API request, it dispatches an action that goes through the API request middleware.

Each middleware focuses solely on its own concerns, which facilitates future maintenance and also provides an excellent way to extend functionality. The View layer only needs to dispatch actions and then render the page with the data, without needing to worry about other logic.

An Example#

Overall Process

Let’s assume we have the following routing configuration.

{
    component: App,
    path: '/',
    onEnter: initLogin(store),
    indexRoute: {
      getComponent(nextState, cb) {
        require.ensure([], require => {
          cb(null, require('../views/Home').default)
        }, 'Home')
      },
      onEnter: initHome(store)
    },
    childRoutes: [
      createActivateRoute(store),
      {
        path: 'test',
        indexRoute: {
          getComponent(nextState, cb) {
            require.ensure([], require => {
              cb(null, require('../views/Test').default)
            }, 'Test')
          }
        }
      },
      ...
    ]
}

Now, let’s look at a complete process in conjunction with react-router. When we enter example.dai.163.com/index.html/#/ in our browser:

First, as mentioned in the Production Environment section above, we retrieve the necessary HTML, CSS, and JS for the page.

Then, the Provider and Router components are rendered, providing store injection and routing control, respectively.

At this point, the root path route matching is triggered, loading the root component APP. Then, according to the routing rules, IndexRouter is matched, and the Home component is loaded.

The subsequent steps are the same as described in the Data Flow section.

Summary#

Building upon a complete frontend-backend separation, a robust development environment can significantly boost our development efficiency and reduce the cost of frontend-backend integration.

Furthermore, by leveraging Redux principles to implement a unidirectional data flow, we can achieve a very clear data flow. And with the help of middleware, we can more effectively control the data flow process, providing endless possibilities for future project expansion.

This article was published on May 12, 2017 and last updated on May 12, 2017, 3068 days ago. The content may be outdated.

React-based Frontend Project Development Summary
https://blog.kisnows.com/en-US/2017/05/12/react-base-project-summary/
Author
Kisnows
Published at
2017-05-12
License
CC BY-NC-ND 4.0