Project Preview Address

preface

I believe that many partners are likely to encounter the development of background management system such requirements, so how can we quickly complete this requirement

This paper will take React as the starting point to record the process of creating a basic management system template, so as to deepen the understanding of react technology stack and actual project, and I hope it will be helpful for you to develop such a project

If there are flaws and mistakes in the article, please also see the small partners to give advice, thank you in advance

The following left

Project introduction

React-admin is a background management system template built by create-React-app scaffolding and based on the React ecosystem. Log-in/logout, lazy route loading, AXIOS encapsulation, simple permission management and other functions, it can help you quickly generate management system templates, you only need to add specific business code

Online preview address Preview address

GitHub code address

Technology stack

The technology stacks involved in this project mainly include ES6, React, React-Router, redux, React-Redux, Create React App, React-Loadable, axios, etc., so you may need to know these knowledge in advance. This will help you understand the project a lot

The basic function

  • The route was loaded lazily. Procedure
  • Breadcrumb navigation
  • The commonly usedUIshow
  • echartsFull screen display
  • Login/logout function
  • axiosencapsulation
  • Simple Rights Management

The project structure

├ ─ ─ the publicResource files that do not participate in compilation├ ─ ─ the SRC# Main program directory│ ├ ─ ─ API# axios encapsulation│ ├ ─ ─ assets# Resource files│ │ ├ ─ ─ the font# font file│ │ └ ─ ─ images# Image resources│ ├ ─ ─ the components# Global public component│ │ ├ ─ ─ CustomBreadcrumb# Breadcrumb navigation│ │ └ ─ ─ CustomMenu# menu menu│ ├ ─ ─ contatiners# Page structure component│ ├ ─ ─ routes# Route directory│ ├ ─ ─ store# redux configuration│ ├ ─ ─ style# Style directory│ ├ ─ ─ utils# tools│ ├ ─ ─ views# the UI pages│ ├ ─ ─ APP. Js# App.js│ └ ─ ─ index. Js# index.js├ ─ ─. Prettierrc. Js# Code specification├ ─ ─ the config - overrides. Js# ANTD styles are loaded on demand
Copy the code

The overall train of thought

Build any project, except for the audience that needs to be considered in the early stage. On top of that, add technology selection, and then you get to the page architecture layer. In this project, the early stage is not on our radar (it has been determined), so we start with the page architecture

A backend management project, whether it has permission validation or not, has some common parts. Such as landing pages, common headers, sidebar navigation, bottom, and error pages. These common parts as well as permissions specific parts together make up the system

After dividing such modules, the basic page architecture of a project is completed, because this part relates to our definition of page routing later, so I think it is an important part

routing

Based on the [email protected]

The routing function can be said to be the key of a React project. By dividing the page architecture above, we can smoothly register the routing

basic

In the React-admin project, the

was used.

First, we need to distinguish between public pages and possibly unique pages

<Router>
    <Switch>
        <! Is the exact match on the home page -->
        <Route path='/' exact render={()= > <Redirect to='/index' />} / ><! -- Error page -->
        <Route path='/ 500' component={View500} />
        <Route path='/login' component={Login} />
        <Route path='/ 404' component={View404} />
        <! -- UI page -->
        <Route path='/' component={DefaultLayout} />
    </Switch>
</Router>
Copy the code

We can then register the routing table and loop through it to our viewport page. You may also have noticed the term loop traversal. Manipulating an array means that we can do a series of filters on it to control routing permissions (which we’ll talk about later).

Lazy loading

As a SPA-level application, there are many advantages (faster response, good front and rear end separation, etc.), but there are also many drawbacks, such as long first load times, which we had to deal with

Since webpack4.0, it has implemented on-demand loading of components, but it has its own rules (such as file size), so we still need to do something with the first page load, and routing is a good place to start

React-loadable is used in this project, which can realize lazy loading of routes in a very simple way, setting the delay time, loading animation, server rendering and other functions

import Loadable from 'react-loadable';
import Loading from './my-loading-component'; Loading const LoadableComponent = Loadable({loader: () => import('./my-component'),
  loading: Loading,
});

export default class App extends React.Component {
  render() {
    return<LoadableComponent/>; }}Copy the code

Of course, you can also use React. Lazy and Suspense techniques (portals), however, it does not support server rendering

The login

The logon logic is simple:

The home page of our project is usually the index page. At this time, we need to first determine whether a user is in the login state when loading in. If so, it will be displayed normally

In this project, the login information of the user is stored in localStorage, and the localStorage is cleared when the user logs out

The token can be directly stored in the local, the background set an expiration time can be

In another case, after the user logs in, the token expires because there is no operation for a long time. In this case, there may be two options:

  • Let the user directly go to the login page to log in again
  • Check whether the local storage user information, if yes, update the user informationtokenTo continue, or to go to the login page (depending on where you store the user information)

Of course, exactly how to do it depends on the demand of the product, but this is just an idea, right

Axios encapsulation

Basic operation

The project uses AXIos to interact with the background, encapsulating functions such as adding request interceptors, response interceptors, setting response times, and adding tokens to requests

import axios from 'axios'

// This depends on where to store the token during login
const token = localStorage.getItem('token')

const instance = axios.create({
    timeout: 5000
})

// Set the POST request header
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

// Add a request interceptor
instance.interceptors.request.use(
    config= > {
        // Add the token to the request header
        token && (config.headers.Authorization = token)
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

// Add a response interceptor
instance.interceptors.response.use(
    response= > {
        if (response.status === 200) {
            return Promise.resolve(response)
        } else {
            return Promise.reject(response)
        }
    },
    error => {
        // Appropriate error handling
        // For example: token expired, no access permission, path does not exist, server problem, etc
        switch (error.response.status) {
            case 401:
                break
            case 403:
                break
            case 404:
                break
            case 500:
                break
            default:
                console.log('Other error messages')}return Promise.reject(error)
    }
)

export default instance

Copy the code

BaseUrl is not set here, mainly considering that there may be more than one URL in the project, such as pictures and other resources may exist on servers such as Qiniuyun or Aliyun, while the background interface is another URL. So a config file was added to export the individual urls

// Consider that the site may have several domain names, so mention them separately

export const API = 'http://rap2api.taobao.org/app/mock/234047'

export const URLAPI = ' '

Copy the code

You can just do that when you call the interface

import {API} from './api/config'
import axios from './api'

axios.get({
    url: API + '/login'
})
Copy the code

Of course, if you do not have such a requirement, you can cancel the config file and encapsulate the baseUrl

Similarly, common requests such as GET, POST, and so on are not encapsulated, because these methods may be used to do some specific operations on the data, such as serialization, and so on, so personal sense is not very significant

Cross domain

Here, by the way, is a note of how cross-domain problems are solved:

If you don’t use NPM run eject to expose the webPack configuration, you can configure the proxy directly in package.json

"proxy": {
    "/api": {
      "target": "http://100.100.100.100".// Back-end address
      "changeOrigin": true}}Copy the code

Just add the/API after baseUrl

Of course, if you expose the configuration in WebPack, you can also set it directly in the Config file to achieve the same effect

permissions

Permission function is basically an indispensable part of background management projects

In general, the control of permissions is reflected in the page level and button level (whether a user can access a page or operate a button, such as add or delete). This permission is determined when the user is registered, assigned, or later changed by the super administrator

Project implementation

In this project to use a simple permission control, I hope to give you some ideas, specific implementation:

  • User login, from the background to obtain the registration role (permission ID)
  • By permission identification, to the registeredmenuThe menu is filtered to render to the page
getMenu = menu= > {
        let newMenu,
            auth = JSON.parse(localStorage.getItem('user')).auth // Obtain the user permission id of the storage
        if(! auth) {return menu
        } else {
            // Filter the registered menu
            newMenu = menu.filter(res= >res.auth && res.auth.indexOf(auth) ! = =- 1)
            return newMenu
        }
    }
Copy the code
  • The registered route array is filtered by the permission id
{routes.map(item => {
    return (
        <Route
            key={item.path}
            path={item.path}
            exact={item.exact}
            render={props= >! auth ? (<item.component {. props} / >
                ) : item.auth && item.auth.indexOf(auth) !== -1 ? (
                    <item.component {. props} / >) : (// You can also jump to the 403 page<Redirect to='/ 404' {. props} / >
                )
            }></Route>)})}Copy the code
  • Button permission, directly use the permission identifier to determine whether it can be operated, hidden or displayed

Note: Here for the registered route array to filter this step to explain, generally front-end routes are registered in advance, even if there is no menu menu navigation, if we directly input the path in the address bar can also access, here after a filter can avoid this situation. Of course, we can also set an array of access paths for each permission, by comparing whether the jump address exists in the array to display the corresponding

The background control

Here is also a brief background control permissions case, for the front end is much simpler

  • The user logs in and gets what needs to be shownmenuArray, rendering directly to the page (filtering the menu is done in the background)
  • The user can determine whether the user has the operation permission of a button based on the permission id

As for the user to enter the address directly in the address bar to access, there are two cases:

  • If the user does not have access to a page, use ittokenThe request for background data must be unsuccessful, we can encapsulate this operation inaxiosIn the request, different status codes are used to jump to the page
  • I just visited an unrequested page (the page is not available to people who do not have permission to see it), so we will filter the permission array to prevent its operation

Of course, the second one here is rare…

other

The project also integrates functional requirements that you might encounter on a regular basis

animation

Animation uses Animate. CSS animation library, the way to use it

/ / download
yarn add animate.css

// The link tag is imported
<link rel="stylesheet" href="animate.min.css">

// Or import import
import 'animate.css'
Copy the code

Then add the corresponding class name on the box you need, you can set the entry, exit animation, animation time, delay and so on

The rich text

The rich text editor uses the official Antd recommendation, Braft-Editor

A draft-Js-based rich text editor for the Web. It works with the React framework and is compatible with mainstream modern browsers

Usage:

/ / introduction
import BraftEditor from 'braft-editor'

/ / can use BraftEditor createEditorState method to raw data into editorState data or HTML format
editorState: BraftEditor.createEditorState('Hello, lovely person! Lucky to meet you here! ')
Copy the code

You can refer to the documentation portal for more details on component properties and instance methods

echarts

Echarts I believe you are not unfamiliar with, Baidu’s documentation is also very clear, here alone to record the main development process encountered a problem

We can use it when the page window changes

window.addEventListener('resize'.function() {
    myChart.resize()
})
Copy the code

This ensures that echarts are normally adaptive

The window. Resize method is not triggered when we click the shrink/expand button. The width of the page box has changed, but the echarts resize event has been triggered. At this point, we only need to register a timer in the componentDidUpdate life cycle to trigger the resize event, just don’t forget to clear the timer in the componentWillUnmount life cycle

Loading progress bar

The loading progress bar uses nprogress and is easy to use

/ / download
yarn add nprogress

/ / introduction
import NProgress from 'nprogress'

// Start loading
NProgress.start();

// Loading is complete
NProgress.done();

// Remove the progress bar
NProgress.remove();
Copy the code

Full screen plug-in

The full-screen feature uses the ScreenFull plug-in

use

/ / download
yarn add screenfull

if (screenfull.isEnabled) {
	screenfull.request(); / / full screen
}

.exit() / / exit
.toggle() / / the swappable
Copy the code

You can also register a change event for it

if (screenfull.isEnabled) {
	screenfull.on('change', () = > {console.log('Am I fullscreen? ', screenfull.isFullscreen ? 'Yes' : 'No');
	});
}
Copy the code

But don’t forget to remove this event

screenfull.off('change', callback);
Copy the code

Uniform code format

The Create React App provides a set of the most common error rules that prompt error messages when code is running, so Eslint rules are not required here. If you also want to prompt errors when code is being written, do the following:

Download eslint

yarn add eslint
Copy the code

Add an.eslintrc.js file or add the rules you need to use directly to the eslintConfig object in the package.json file

More rules can be found here

Eslint was not used in the project, but pretter was added to unify the coding style of the project

As for how to integrate Pretter into a project, you can refer to the official document for the specific usage, which will not be described here

Webpack expand

I believe that when you are developing such a project, there will be more or less some customization things need to modify the webPCK configuration

When creating a project using the Create React App, the Webpack configuration is not exposed by default. If we want to change the configuration, we can use yarn eject or NPM run eject to expose it and change it

But what if we want to configure WebPack without exposing it

Antd’s style on demand loading is implemented in this way

React-app-rewired is an open source tool for modifying CRA configurations in the React community, such as extending the Create React App Webpack configuration, Customize-cra provides a set of Create React App configurations for customizing the react-app-rewired core functionality, which can be extended to the WebPack configuration through the config-overrides

Keep it simple: React-app-rewired and Customize-cra fulfill our needs

Usage:

  • You must have downloaded these two packages first
  • Then create one in our project root directoryconfig-overrides.jsfile
For example, we set an absolute path

const { override, addWebpackAlias } = require('customize-cra')
const path = require('path')

module.exports = override(    
   addWebpackAlias({        
       [The '@']: path.resolve(__dirname, 'src')}))Copy the code
  • Last modifiedpackage.jsonIn the filescripts
"scripts": {
    "start": "react-app-rewired start"."build": "react-app-rewired build"."test": "react-app-rewired test"."eject": "react-scripts eject"
}
Copy the code

End of story, so we can happily use this @ in our project

Of course, the customize-cra package also provides us with many encapsulated apis, as you can see here

The last

This project was developed in my spare time, mainly to get familiar with react development process and the use of surrounding ecology. The project is still relatively simple, and iterative development will be carried out in the later stage to build it into a more practical background management template

If you feel good or have some help for you, welcome to star, or you have a better way to achieve, interesting idea, also welcome to leave a message exchange

If you are interested, you can click here or scan the qr code below to follow my wechat official account and see more front end snippets. Welcome to star