Quickly build web applications and learn the react background project template from scratch

Time:2022-5-3

To build practical applications quickly, we cannot do without a good application template. As a tool produced by large manufacturers, react has the guarantee of stability and maintainability. At the same time, it can use a full set of relevant family buckets (react + react router + Axios + mobx + antd) for coherent and agile development. This paper will start from how to create a project application template on the cloud development platform and create a background management project based on the application template, And upload and deploy the project online through the cloud platform to provide a more convenient operating environment for project development.

1、 Quickly create and initialize applications through cloud development platform

1. To create relevant application templates, please refer to the link: https://developer.aliyun.com/…

2. After creation, you can view the new react warehouse in GitHub
Quickly build web applications and learn the react background project template from scratch

2、 Local preparation of background management project

1. Clone the application template locally

• first, assume that you have installed GIT and node, but if not, please move to the official website of node for installation. Clone project:

Git clone + project address

• access to project documents

cd create-react-app

• switch feature to 1.0/0 branch

git checkout feature/1.0.0

• use the following command to install react globally:

npm install -g create-react-app

• install dependent packages

npm install

• start service

npm start

Here, open the browser 3000 port and the default page appears.

2. Preview of architecture and effect

• project structure of back office management
Quickly build web applications and learn the react background project template from scratch
• effect preview
Quickly build web applications and learn the react background project template from scratch

3. Initialize project

• initialize package json

npm init

• install webpack

npm add -D webpack webpack-cli webpack-merge

The webpack version used in the project is ^ 5.10.0, webpack 4.0 0 package build has made many default optimized configurations, and many configuration items do not need to be configured or changed.
For example: to accelerate the packaging speed of development mode, merge chunk; Code compression for production mode, reduce packaging volume, etc.

//Some default configurations  
optimization: {
        Removeavailablemodules: true, // delete the resolved chunk (default true)
        Removeemptychunks: true, // delete empty chunks (default true)
        Mergeduplicatechunks: true // merge duplicate chunks (the default is true)
    }

 //Default configuration for production environment
    optimization: {
        Sideeffects: true, // cooperate with tree shaking
        splitChunks: {...}, // Unpacking
        Namedmodules: false, // namedchunks: false do not enable chunk naming, default self incrementing ID
        Minimize: true, // code compression
    }

It is very necessary to distinguish the configuration of webpack according to the development environment / production environment. It can speed up the packaging speed of the development environment. Sometimes when the packaging of the development environment is too slow, you can check whether the configuration is wrong (for example, the development environment has turned on code compression).
In the project, cooperate with webpack merge to split and configure according to the development environment / production environment:
Quickly build web applications and learn the react background project template from scratch
Webpack4. 0 has been released for a long time. I believe that basically the projects have been migrated to 4.0. I won’t repeat it here.
• configure HTML templates
Installation:

npm add -D html-webpack-plugin

to configure:

const srcDir = path.join(__dirname, "../src");
plugins: [
    new HtmlWebpackPlugin({
        template: `${srcDir}/index.html`
 })
]

• configure local services and hot updates
Installation:

npm add -D webpack-dev-server clean-webpack-plugin

The development environment uses webpack dev server to build a local web server and enable module hot update (HMR).
In order to facilitate development and debugging, forward proxy requests (in this case, encapsulate the forwarding interface with Axios to the easy mock online platform)
to configure:

Mode: "development", // development mode
Devserver: {// local service configuration
    port: 9000,
    hot: true,
    open: false,
    historyApiFallback: true,
    compress: true,
    Proxy: {// proxy
        "/testapi": {
            target:
            "https://www.easy-mock.com/mock/5dff0acd5b188e66c6e07329/react-template",
             changeOrigin: true,
             secure: false,
             pathRewrite: { "^/testapi": "" }
        }
    }
},
plugins: [
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
],

• configure Babel
Installation:

npm add -D babel-loader @babel/core @babel/plugin-transform-runtime 
 @babel/preset-env @babel/preset-react  babel-plugin-import
 @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators

Babel configuration in webpack is an important part. It is related to whether ES6 syntax, react JSX, mobx and other grammars can run normally after packaging.
Of which:
@Babel / preset react transform JSX syntax;
@Babel / plugin proposal class properties conversion class syntax;
@Babel / plugin proposal decorators transform more advanced syntax such as mobx;
Babel plugin import cooperates to realize the on-demand loading of react components;
Here we need to pay attention to babel7 0 compared to babel6 The difference between 0.
to configure:

module: {
    rules: [
        {
            test: /\.(js|jsx)$/,
            include: [srcDir],
            use: ["babel-loader?cacheDirectory=true"]
        },
    ]
}
• . Babelrc file configuration
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/transform-runtime",
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true
            }
        ],
        ["@babel/plugin-proposal-class-properties", { "loose": true }],
        [
            "import",
            {
                "libraryName": "antd",
                "libraryDirectory": "es",
                "Style": "CSS" // 'style: true' will load less files
            }
        ]
    ]
}

• handle resources such as less styles and pictures
Installation:

npm add -D less less-loader style-loader css-loader url-loader 
mini-css-extract-plugin postcss-loader autoprefixer

Of which:
Less loader, style loader and CSS loader handle and load less and CSS files;
Postcss loader and autoprefixer are compatible with CSS style browser prefix;
URL loader handles images, font files and other resources;
Mini CSS extract plugin separates CSS into separate files;
to configure:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
...
module: {
    rules: [
        {
            test: /\.less$/,
            use: [
                devMode ? "style-loader" : MiniCssExtractPlugin.loader,
                "css-loader",
                "postcss-loader",
                "less-loader"
            ]
        },
        {
            test: /\.css$/,
            use: [
                devMode ? "style-loader" : MiniCssExtractPlugin.loader,
                "css-loader",
                "postcss-loader"
            ]
        },
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            use: ["url-loader"],
            include: [srcDir]
        },
        {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
            use: ["url-loader"],
            include: [srcDir]
        },
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            use: ["url-loader"],
            include: [srcDir]
        }
    ]
},
plugins: [
    new MiniCssExtractPlugin({
        filename: "[name].[contenthash:8].css",
        chunkFilename: "chunk/[id].[contenthash:8].css"
    }),
    ],

Configure postcss postcssrc. JS file

// .postcssrc.js
module.exports = {
    plugins: {
        autoprefixer: {}
    }
};
// package. Configuring compatible browsers in JSON
"browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 10"
]

• multi threaded packaging with happypack
Installation:

npm add -D happypack

to configure:

const os = require("os");
const HappyPack = require("happypack");
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module: {
    rules: [
        {
            test: /\.(js|jsx)$/,
            include: [srcDir],
            exclude: /(node_modules|bower_components)/,
            use: ["happypack/loader?id=happybabel"]
        },
    ]
},
plugins: [
    //Turn on the thread pool of happypack
    new HappyPack({
        id: "happybabel",
        loaders: ["babel-loader?cacheDirectory=true"],
        threadPool: happyThreadPool,
        cache: true,
        verbose: true
    }),
]

• production environment split module
Split the module according to the actual project situation and cooperate with asynchronous loading to prevent a single file from being too large.

optimization: {
        runtimeChunk: {
            name: "manifest"
        },
        splitChunks: {
            Chunks: "all", // it only works on asynchronous modules by default. If it is "all", it will work on all modules, and "initial" will work on synchronous modules
            cacheGroups: {
                dll: {
                    test: /[\\/]node_modules[\\/](react|react-dom|react-dom-router|babel-polyfill|mobx|mobx-react|mobx-react-dom|antd|@ant-design)/,
                    minChunks: 1,
                    priority: 2,
                    name: "dll"
                },
                codeMirror: {
                    test: /[\\/]node_modules[\\/](react-codemirror|codemirror)/,
                    minChunks: 1,
                    priority: 2,
                    name: "codemirror"
                },
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    minChunks: 1,
                    priority: 1,
                    name: "vendors"
                }
            }
        }
    }

• other configurations
Introduce eslint and prettier to cooperate, standardize team project code development and unify code style.

npm add -D prettier babel-eslint eslint eslint-loader eslint-config-airbnb 
eslint-config-prettier eslint-plugin-babel eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react

See details for specific configuration/Build directoryDown https://github.com/now1then/r…
• npm scripts
package. JSON file

{
    ...
    "scripts": {
        "start": "webpack-dev-server --color --inline --progress --config build/webpack.dev.js", //
        "build": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js",
        "build:report": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js",
        "build:watch": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js"
    },
    ...
}

Command line run:
//Command line execution
//Operation and development environment;

npm start

//Packaging and compression of production environment;


npm build

//Graphical analysis of package file size;


npm build:report

//It is convenient to check the error information of the file packaged in the production environment (file source map);


npm build:watch

The functions of build: report and build: watch can be realized in build / webpack Prod.js has the following code:
//It is convenient to check the error information of the file packaged in the production environment (file source map)

if (process.env.npm_lifecycle_event == "build:watch") {
    config = merge(config, {
        devtool: "cheap-source-map"
    });
}
//Graphical analysis package file size
if (process.env.npm_lifecycle_event === "build:report") {
    const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
        .BundleAnalyzerPlugin;
    config.plugins.push(new BundleAnalyzerPlugin());
}

• project code architecture

npm add react react-dom react-router-dom mobx mobx-react mobx-react-router 
axios antd moment

4. Functional hooks

The current version of react has been updated to 16.12. Hooks should become the mainstream of react. In this project, hook will be fully embraced. Generally, class is no longer used to implement components.
The following is part of the implementation code (the use of mobx can be ignored temporarily):

import React, { useState, useEffect, useContext } from 'react';
import { observer } from 'mobx-react';
import { Button } from 'antd';
import Store from './store';
import './style.less';
const HomePage = () => {
    //Usecontext subscription mobx data
    const pageStore = useContext(Store);
    //Usestate state
    const [num, setNum] = useState(0);
    //Useeffect side effects
    useEffect(() => {
        pageStore.qryTableDate();
    }, []);
    return (
        <div className="page-home page-content">
            <h2>{pageStore.pageTitle}</h2>
            <div>
                <span>Num value: {num}</span>
                <Button type="primary" size="small" style={{ marginLeft: 10 }}
                    onClick={() => setNum(num + 1)}
                >+1</Button>
            </div>
        </div>
    );
};
export default observer(HomePage);

5. Router routing configuration

The project is a single page application. Routing configuration is generally divided into agreed dynamic routing and centralized configuration routing.
In the world of react, mature react router tools are directly used to manage page routing. When we talk about react router, we are basically talking about the version after the fourth version of react router. The latest version has been updated to 5.1 It’s X.
At present, react router supports dynamic routing, which is completely realized by react components. Routing rules are dynamically set in the rendering process, and the corresponding page components are loaded by matching the hit rules.
The project adopts centralized configuration routing (convenient route authentication, obtaining menu route configuration from the server interface, etc.), while taking into account the convenience of setting the side menu bar. Of course, for simplicity, read the local static menu configuration in the project, and route authentication has not been introduced yet.

6. Static route configuration Src / routes / config js :

import React, { lazy } from "react";
import BasicLayout from "@/layouts/BasicLayout";
import BlankLayout from "@/layouts/BlankLayout";
const config = [
    {
        path: "/",
        Component: blanklayout, // blank page layout
        Childroutes: [// submenu routes
            { 
                Path: "/ login", // routing path
                Name: "login page", // menu name (if not set, it will not be displayed in the menu bar)
                Icon: "setting", // menu icon
                Component: lazy (() = > Import ("@ / pages / login") // lazy loading of routing components
            },
            //Login and other pages without basic layout such as menu navigation bar should be placed before basic layout.
            {
                path: "/",
                Component: basicplayout, // basic layout framework
                childRoutes: [
                    {
                        path: "/welcome",
                        Name: "welcome page",
                        icon: "smile",
                        component: lazy(() => import("@/pages/Welcome"))
                    },
                    {... / * other * /}, 
                    { path: "/", exact: true, redirect: "/welcome" },
                    { path: "*", exact: true, redirect: "/exception/404" }
                ]
            }
        ]
    }
];
export default config;

Static routing is part of the above configuration,
Note: the package will be used in the, which will match the first one hit. “/ login” and other pages without basic layout such as menu navigation bar should be placed before basic layout.
Use and react Lazy () implements lazy loading of page components.

7. Route composition and rendering Src / routes / approuter js :

import React, { lazy, Suspense } from "react";
import LoadingPage from "@/components/LoadingPage";
import {
    HashRouter as Router,
    Route,
    Switch,
    Redirect
} from "react-router-dom";
import config from "./config";
const renderRoutes = routes => {
    if (!Array.isArray(routes)) {
        return null;
    }
    return (
        <Switch>
            {routes.map((route, index) => {
                if (route.redirect) {
                    return (
                        <Redirect
                            key={route.path || index}
                            exact={route.exact}
                            strict={route.strict}
                            from={route.path}
                            to={route.redirect}
                        />
                    );
                }
                return (
                    <Route
                        key={route.path || index}
                        path={route.path}
                        exact={route.exact}
                        strict={route.strict}
                        render={() => {
                            const renderChildRoutes = renderRoutes(route.childRoutes);
                            if (route.component) {
                                return (
                                    <Suspense fallback={<LoadingPage />}>
                                        <route.component route={route}>
                                            {renderChildRoutes}
                                        </route.component>
                                    </Suspense>
                                );
                            }
                            return renderChildRoutes;
                        }}
                    />
                );
            })}
        </Switch>
    );
};
const AppRouter = () => {
    return <Router>{renderRoutes(config)}</Router>;
};
export default AppRouter;

8. Routing hooks syntax

React router DOM also supports hooks syntax to obtain route information or route jump. You can use the new hooks function:
• usehistory: obtain historical route, fallback, jump and other operations;
• uselocation: view the current routing information;
• useparams: read the params parameter information attached to the route;
• useroutematch: match the current route;
As long as the sub components wrapped in the can obtain routing information through these hook functions.
Code demonstration:

import { useHistory } from "react-router-dom";
function HomeButton() {
    const history = useHistory();
    function onClick() {
        history.push("/home");
    }
    return (
        <button type="button" onClick={onClick}>
            Jump to home page
        </button>
    );
}

9. Manage data status in combination with mobx

Whether to use status management tools or what kind of management tools to use in the project depends on the actual situation of the project.
This project uses the familiar mobx, which is a powerful and easy to use state management tool.
In order to simplify use and facilitate management, the organization is divided into global public data state and page data state.
Public data status is stored in / SRC / stores directory; Page data is stored in the corresponding page directory.
In implementation, the feature of mobx + usecontext hook is used to realize the state management of functional components.
Specifically, the createdcontext of react is used to build the context containing mobx; Use usecontext hook in functional components to subscribe to mobx data changes.
• page level store JS code:

import { createContext } from "react";
import { observable, action, computed } from "mobx";
import request from "@/services/newRequest";
class HomeStore {
    @observable tableData = [];
    @Observable PageTitle = "home page";
    @observable loading = false;
    @action.bound setData(data = {}) {
        Object.entries(data).forEach(item => {
            this[item[0]] = item[1];
        });
    }
    //Data list
    @action.bound
    async qryTableDate(page = 1, size = 10) {
        this.loading = true;
        const res = await request({
            url: "/list",
            method: "post",
            data: { page, size }
        });
        if (res.success) {
            const resData = res.data || {};
            console.log(resData);
        }
        this.loading = false;
    }
}
export default createContext(new HomeStore());

• page component code:

import React, { useContext } from "react";
import { observer } from "mobx-react";
import Store from "./store";
import "./style.less";
const HomePage = () => {
    const pageStore = useContext(Store);
    return (
        <div className="page-home page-content">
            Home page
            <h2>{pageStore.pageTitle}</h2>
        </div>
    );
};

export default observer(HomePage);
The above is part of the demonstration code. You can view the project code for specific business implementation.

10. Axios HTTP request encapsulation

Axios request encapsulation. See / SRC / services / newrequest for specific code js

11. UI components and page layout

UI components use excellent ant design component library. Pay attention to using Babel plugin import configuration to load components on demand.
The internal page layout of the project adopts the classic layout method on antd:
Quickly build web applications and learn the react background project template from scratch
The page layout needs to split the modules reasonably, and the left menu navigation bar is rendered according to the static menu. See the project for the actual complete code, and the following is the basic layout component:

import React from "react";
import { Layout } from "antd";
import SiderMenu from "../SiderMenu";
import MainHeader from "../MainHeader";
import MainFooter from "../MainFooter";
import "./style.less";
const BasicLayout = ({ route, children }) => {
    return (
        <Layout className="main-layout">
            {/ * left menu navigation * /}
            <SiderMenu routes={route.childRoutes} /> 
            <Layout className="main-layout-right">
                {/ * top display layout * /}
                <MainHeader></MainHeader>
                <Layout.Content className="main-layout-content">
                    {/ * actual page layout * /}
                    {children}
                    {/* <MainFooter></MainFooter> */}
                </Layout.Content>
            </Layout>
        </Layout>
    );
};
export default BasicLayout;

The basic layout of the login page and other pages that do not need to be set on it needs to be handled separately (the menu configuration is before the basic layout configuration).
Quickly build web applications and learn the react background project template from scratch

3、 One click deployment of online applications in the cloud

1. Upload code

git add . 
Git commit - M 'add your comment'
git push

2. Deploy in daily environment

One click application deployment. On the application details page, click the “deploy” button of the daily environment for one click deployment. The deployment status turns green. After deployment, you can click to visit the deployment website to view the effect.
Quickly build web applications and learn the react background project template from scratch

3. Configure custom domain name online environment

• configure the online environment and customize the domain name. After the function development verification is completed, deploy in the online environment, and fill in your own domain name in “deployment configuration” – “custom domain name” of the online environment. For example, we add a secondary domain name company workbench. Fun to bind our deployed front-end applications. Then copy the API gateway address under the custom domain name and configure the CNAME of the added secondary domain name.
Quickly build web applications and learn the react background project template from scratch
• configure CNAME address. After copying the domain name address of the API gateway, go to your own domain name management platform (the domain name management in this example is Alibaba cloud domain name management console, please go to your own domain name console). Select “CNAME” for the “record type” of the added record, enter the secondary domain name you want to create in the “host record”, here we enter “company”, paste the API gateway domain name address we copied before in the “record value”, and “TTL” can keep the default value or set a value you think appropriate.
Quickly build web applications and learn the react background project template from scratch
• online environment deployment. Go back to the application details page of the cloud development platform, click the “deployment button” of the online environment according to the deployment operation. After the deployment is completed, it will be launched in your customized domain name. After CNAME takes effect, we enter company workbench. Fun (sample URL) can open the deployed page. So far, how to deploy an application to the online environment and how to bind your domain name to access an online application is completed. Quickly deploy your application to the online environment and play with your domain name;)
Quickly build web applications and learn the react background project template from scratch
Link to create react application template with one click: https://workbench.aliyun.com/…

reference: https://juejin.cn/post/684490…