Full project address: ice-fusion-admin

preface

Why do I want to do this project? In a nutshell, two purposes. One is for precipitation. At present, I have done a lot of React middle and background management projects and found some commonalities in these projects, but there is a lack of precipitation projects and opportunities. Therefore, I recently decided to build a React background management project startup template from scratch to summarize the excellent methods I have learned in existing projects. In addition, some friends may find that the name of the project is a familiar one. Yes, it refers to the vue-element-admin project, which can be said to be the best middle and background management template in the VUE technology stack. At the beginning, this project greatly improved my vUE development level, which is also the second reason for me to do this project. To help students who are new to the React stack, we hope that we can learn from each other in this project.

Initialize an Ice base project

Start by initializing a simple Ts project using ice CLImkdir ice-fusion-admin && cd ice-fusion-admin npm init iceSelect a simple templateperformnpm install && npm startThen you can see our initialized project in the browserThen open the project file in Vscode and you can see the following file structure. If you need additional customization for the project configuration, you can write it in the build.json file. All configuration item documents:Ice. Work/docs/config… 。

├─ │ ├─ ├─ public/ │ ├─ ├.html # ├─ favicon # Favicon ├─ SRC / # source path │ ├─ Components / # Partial page components, please in the corresponding page directory components │ │ └ ─ ─ Guide / │ │ ├ ─ ─ index. [j, t] sx │ │ └ ─ ─ index. The module. The SCSS │ ├ ─ ─ pages / # page │ │ └ ─ ─ index. The TSX / │ ├ ─ ─ global. The SCSS global style # │ └ ─ ─ app. [j, t] [x] # s application gateway script ├ ─ ─ the README. Md ├ ─ ─ package. The json ├ ─ ─ the editorconfig ├─.Eslintignore ├─.Eslintignore.[J,t]s ├─.Eslintignore ├─.Eslintignore.[j,t]s ├─.Eslintignore ├─.Eslintignore Plug-in configuration file, prettier is a beautify code plug-in ├ ─ ─ the gitignore ├ ─ ─ the stylelintignore ├ ─ ─ the stylelintrc. [j, t] s ├ ─ ─ the gitignore └ ─ ─ [j,t]sconfig.json # js/ts config file, configure js version, compile options, etcCopy the code

Installing Prettier is recommended and negotiating a common Prettier configuration file with the team so coding can be uniformIf the.prettierrc.js configuration file is not generated in the project, you can manually configure it after installing the plug-in

// .prettierrc.js
const { getPrettierConfig } = require('@iceworks/spec');
// Use the react configuration for Prettier provided by ICE directly, as we used it in our project
module.exports = getPrettierConfig('react');
Copy the code
// .prettierignore
build/
tests/
demo/
.ice/
coverage/
**/*-min.js
**/*.min.js
package-lock.json
yarn.lock
Copy the code

Introduce the Fusion Next component library

The next step is to introduce fusion’s Next component library into the project. First install the Next component library. NPM install build-plugin-fusion –save-dev We can build on custom fusion All supported configuration items can be viewed at ice.work/docs/plugin… Once installed, configure the plug-in in the build.json file

{
  "plugins": [["build-plugin-fusion",
      {
        "themePackage": "@alifd/theme-design-pro"}}]]Copy the code

Then check to see if the configuration is successful by introducing the Next component library button into the project

//src/components/Guide
import * as React from 'react';
import { Button, Box } from '@alifd/next';
import styles from './index.module.scss';

const Guide = () = > {
  return (
    <div className={styles.container}>
      <h2 className={styles.title}>Welcome to icejs!</h2>
      <Box direction="row" spacing={20} justify="center">
        <Button type="normal">Normal</Button>
        <Button type="primary">Prirmary</Button>
        <Button type="secondary">Secondary</Button>
      </Box>
      <p className={styles.description}>This is a awesome project, enjoy it!</p>
    </div>
  );
};

export default Guide;
Copy the code

You can see that the Next component library has been successfully introduced

Add a Layout to the application

A layout is the outermost structure of a product. It usually contains navigation, sidebars, breadcrumbs, and content. To understand a back-end project, you need to understand its basic layout. Generally speaking, the non-business “application content” section is a Layout: navigation menus, user information, page headers, breadcrumb navigation, etc.

Use the Layout component provided in the @alifd/ Fusion-design-pro-js template to create a new layouts directory in the SRC directory to store the Layout file

Here we use two layouts, one is BasicLayout, which contains the navigation, sidebar, breadcrumbs and other components of the application layout, and the other is UserLayout, which is used for user login, registration and other pages.

Routing management

There are two ways to use contracted routing and configured routing in ice.js. It is officially recommended to use configured routing. The routing information of applications is configured in SRC /routes.ts. Configuration routing: ice.work/docs/guide/…

Work /docs/guide/…

We created login and project home page (Dashboard), and combined with the imported layout component above, the completed routing configuration file is as follows:

import React from 'react';
import { IRouterConfig } from 'ice';
import UserLayout from '@/layouts/UserLayout';
import BasicLayout from '@/layouts/BasicLayout';
import Login from '@/pages/Login';
import Workplace from '@/pages/Workplace';
import FeedbackNotFound from '@/pages/FeedbackNotFound';

// Lazy route loading
const Register = React.lazy(() = > import('@/pages/Register'));

const routerConfig: IRouterConfig[] = [
  {
    path: '/user'.component: UserLayout,
    children: [{path: '/login/:username'.component: Login,
      },
      {
        path: '/register'.component: Register,
      },
      {
        path: '/'.redirect: '/user/login',},],}, {path: '/'.component: BasicLayout,
    children: [{path: '/dashboard/workplace'.component: Workplace,
      },
      {
        // 404 No route was matched
        component: FeedbackNotFound,
      },
    ],
  },
];

export default routerConfig;
Copy the code

However, in actual projects, we will split routes from different sub-application/sub-business levels, unify them and export them outward:

Finally, in SRC /app.ts, we can configure the route type and base path

import React from 'react';
import { runApp, IAppConfig } from 'ice';

const appConfig: IAppConfig = {
  router: {
    type: 'browser'.// The route type browser is BrowserHistory and hash is HashHistory
    basename: '/seller'.// Unified path prepath
    fallback: <div>loading...</div>.// Load components with routing lazy loading
    modifyRoutes: (routes) = > {
      // Dynamic routing configuration
      returnroutes; }},app: {
    rootId: 'ice-container',}}; runApp(appConfig);Copy the code

The methods of jumping to different routes and obtaining parameters in an application are basically the same as those of using the React – Router. In fact, the routing function of Ice is also based on the encapsulation of the React – Router

HashHistory and BrowserHistory

Front-end routes are typically implemented in two ways: HashHistory and BrowserHistory, both with # indicating that HashHistory is used. Advantages and disadvantages of these two methods:

Features \ Scheme HashHistory BrowserHistory
aesthetic No, it has a hashtag good
Ease of use simple Medium, requires server cooperation
Depends on the server configuration Do not rely on Rely on
Conflicts with anchor points conflict Don’t conflict
compatibility IE8 IE10
State transfer parameter Does not support support

Many environmental

By default, ICE supports both start/build and icejs start/build by default. For example, the current project of our department usually has three sets of daily, pre-delivery and online environments, and different plug-ins need to be started under different compilation environments. In THE ICE project, we can set different environments by using the –mode parameter. For example, I added pre and daily modes. Disable the mock service in and configure both modes

Encapsulate a request method

I’ve written a detailed article about the encapsulation process before. If you are interested, you can see 👀. Encapsulate a caching request method based on Axios and create a utils folder in the SRC directory to store the public methods needed in your project

Manage all interface modules

Generally, middle and background projects involve a large number of interfaces, and reasonable management of these interfaces is conducive to better maintenance of the project, which is part of the Api module in my current project

As you can see, there are many modules, and as the business iterates, there will be more and more modules. Therefore, it is suggested to divide pages according to service modules, establish a service folder under the SRC directory to place API modules, and map pages and Service modules one by one to facilitate maintenance. The diagram below:

In this project we will start with a simple login interface module

Cross-domain problem

The longest cross-domain problem encountered in front and back end interaction is solved by visiting front-end resources of back-end address proxy, that is, directly accessing the page URL address provided by back-end service, and then debugging resources loaded in the page by proxyIce also provides support for this solution

  1. Through the ICEJS plug-in build-plguin-smart-debug
  2. Chrome plug-ins xswitch

There are other solutions using CROs and local proxies, but croS requires the support of the back end, so it’s up to the team to negotiate easily. In this project, we used local proxies to solve cross-domain problems. Other cross-domain solutions can be found at: ice.work/docs/guide/…

We can configure proxy fields in build.json files to control cross-domains. This approach is suitable for simple configuration, but it is not sufficient. Ice’s cross-domain solution is essentially based on HTTP-proxy-Middleware. Therefore, the supported configuration items are the same as http-proxy-Middleware. However, json files do not support function configuration items (of course, you can also open JS configuration files, and you need to specify the configuration file in NPM scripts). Here we can implement the function in the form of plug-ins, using a separate configuration file to control cross-domain configuration. Create the setupproxy. ts configuration file in the root directory and write the configuration content:

const merge = require('lodash/merge');

module.exports = ({ context, onGetWebpackConfig }) = > {
  onGetWebpackConfig((config) = > {
    // Enable the mode of the proxy. See: https://ice.work/docs/guide/basic/config
    // Automatically use different agents according to the configuration of different mode startup
    const proxyModes = {
      / / all the parameters of the support, detailed configuration tutorial at https://github.com/chimurai/http-proxy-middleware
      pre: {
        '/api': {
          target: 'http://www.preServer.org',},'/preOnly': {
          target: 'http://www.preOnlyServer.org',}},daily: {
        '/api': {
          target: 'http://www.dailyServer.org',}}};const { mode } = context.commandArgs;
    if(! proxyModes[mode]) {return;
    }
    const proxyRules = Object.entries(proxyModes[mode]);
    const originalDevServeProxy = config.devServer.get('proxy') | | [];if (proxyRules.length) {
      const proxy = proxyRules
        .map(([match, opts]) = > {
          const{ target, ... proxyRule } = opts;return merge(
            {
              target,
              changeOrigin: true.logLevel: 'warn'.onProxyRes: function onProxyReq(proxyRes, req) {
                proxyRes.headers['x-proxy-by'] = 'ice-proxy';
                proxyRes.headers['x-proxy-match'] = match;
                proxyRes.headers['x-proxy-target'] = target;

                let distTarget = target;
                if (target && target.endsWith('/')) {
                  distTarget = target.replace(/ / / $/.' ');
                }
                proxyRes.headers['x-proxy-target-path'] = distTarget + req.url;
              },
              onError: function onError(err, req, res) {
                // proxy server error can't trigger onProxyRes
                res.writeHead(500, {
                  'x-proxy-by': 'ice-proxy'.'x-proxy-match': match,
                  'x-proxy-target': target,
                });
                res.end(`proxy server error: ${err.message}`);
              },
            },
            proxyRule,
            { context: match }
          );
        })
        .filter((v) = > v);
      config.devServer.proxy([...originalDevServeProxy, ...proxy]);
    }
    // http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
  });
};
Copy the code

Finally, import setupproxy.ts as a plug-in in build.jsonThis will automatically execute the proxy script before the project is compiled. This configuration file is essentially a plugin for ice.js. If you have other engineering configuration requirements, you can also make a custom plugin like this and use it directly in build.json. See 🔎 for detailsIce plug-in development guide

Front-end and back-end interactions and Mock data

From my own development experience, communication costs take up a large part of our time in normal development, but a good way of collaborating on the front and back end can solve a lot of time. In our development process, the front and back end meet with the product to discuss the project. After that, the back end first defines data formats and APIS according to the requirements. Some complex interfaces will be discussed with our front end. After the interface specification is set, the front-end will first develop some page UI. After the Swagger document is written in the back-end, we will generate mock data of the interface through Pont (a data debugging platform) for data interactive development. As a matter of fact, Pont is really easy to use and highly recommended, especially for ts development projects. Saves a lot of communication costs at the front and back end. However, in rare cases where swwager is not written on the back end, native Mock services are usually used directly. Ice also provides Mock services out of the box. See documentation: ice.work/docs/guide/… We will create a mock folder in the root directory of our project. All js/ts files in the mock/ folder will be automatically loaded by the framework, so you can split the interface into multiple mock files. Then create index.ts under the Mock/folder to hold all the common mock interface configurations.

Of the pit

Build complex React backend project from scratch. The complete project address is ice-fusion-admin

In the follow-up updates, please follow 💕~