Quick start guide of umi+dva project

In the react project, there are more and more people using react+umi+dva+antd, a series of Ali technology stacks. This article will share the first-hand tutorial of the umi project.

Construction project

node environment

node version > = 8.0.0

Global installation umi

npm install -g umi

yarn installation is recommended

// Global installation of yarn
npm install -g yarn

// Using yarn to install umi
yarn global add umi

Building a umi project

mkdir myapp && cd myapp
yarn create umi

For detailed construction, please refer to Construction of official project of umi

directory structure

├── config/
    ├── config.js                  // umi configuration, the same as. umirc.js, two choices
├── dist/                          // Default build output directory
├── mock/                          // The directory of the mock file, based on express
├── public/                        // Global relative path file
└── src/                           // Source directory, optional
    ├── assets/                    // Static file
    ├── components/                // Global common components
    ├── layouts/index.js           // Global portal file
    ├── models/                    // Global models file to store global shared data store
    ├── pages/                     // Page directory, business components
        ├── .umi/                  // dev temporary directory, to be added to. gitignore
        ├── .umi-production/       // build temporary directory, which will be deleted automatically
        ├── index/                 // Home page module
        ├── manager/               // Management module
            ├── components/        // Management side - local common components
            ├── models/            // Management side - local models to store the manager's store
            ├── services/          // Management side - local services, where the manager interface is stored
            ├── index.js           // Business component index
            ├── page.js            // Business component page
            ├── _layout.js         // Local entry file
        ├── 404.js                 // 404 page
    ├── services/                  // Global services file to store global public interface
    ├── utils/                     // Global tool class
    ├── global.css                 // The global style file of the contract can be imported automatically. You can also use global.less
    ├── global.js                  // The contracted global Js file is imported automatically. polyfill can be added here
    ├── app.js                     // Runtime profile
├── .umirc.js                      // umi configuration, the same as config/config.js, choose one of the two
├── .env                           // environment variable
└── package.json

configuration file

umi allows configuration in. umirc.js or config/config.js (one out of two,. umirc.js takes precedence), and supports ES6 syntax. This article uses config/config.js

export default {
  base: '/web/',  //Configuration is required when deploying to a non root directory
  targets: { //Configure the minimum version of browser, such as ie11 compatible
   ie: 11
  },
  hash: true,  //Turn on the hash suffix of the package file
  treeShaking: true, //Remove code that is referenced but not used
  plugins: [
    [
      'umi-plugin-react',
      {
        antd: true, //After enabling, Babel plugin import will be automatically configured to load antd on demand
        dynamicImport: { //Dynamic loading at routing level
          webpackChunkName: true //Implement meaningful asynchronous filenames
        },
        dva: {
          dynamicImport: true, //Enable on demand loading or not
          hmr: true //Whether to enable hot update of dva
        },
        //A dll file is prepackaged by the dll plug-in of webpack to achieve the purpose of twice starting and speeding up
        dll: {
          exclude: [],
          include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch', 'antd/es']
        },
        //Only reference is needed when the contract route is used to ignore the automatically generated route in the specified folder
        routes: {
          exclude: [
            /components\//,
            /model\.(j|t)sx?$/,
            /components\.(j|t)sx?$/,
            /service\.(j|t)sx?$/,
            /models\//,
            /services\//
          ],
        },
      }
    ]
  ],
  //When configuring routing, the routing file is referenced here (I'll talk about it later)
  routes: routes,
  //Agent request
  proxy: {
    "/api": {
      "target": "http://jsonplaceholder.typicode.com/",
      "changeOrigin": true,
      "pathRewrite": { "^/api" : "" }
    }
  },
  alias: {'@': resolve(__dirname, '../src'),} //Alias, umirc.js is' src '
};

pages

+ pages
  + manager
    + components/
    + models/
    + services/
    - index.js
    - _layout.js  

In the business directory, the standard configuration is local common components, local common data models, local common interface services, and the [layout. JS] entry file needs to be imported.

Route

1. Conventional routing

1.1 according to the UMI convention, the. js/.jsx in the pages directory will automatically generate a route. Except for the directories or files exclude d in the plugins/routes configuration file, the path of the file is the route.
1.2 src/layout / is a global layout. It is a global entry file by default. It is invalid under the configured route. The "layout.js" under any file under pages / is the entry file under the current folder route. You must go through "layout.js" before entering the current folder route

2. Configured routing

In the configuration file. umirc.(ts|js) or config/config.(ts|js), introduce:

export default {
  routes: [
    {
      path: '/',
      component: '../layouts/index',
      routes: [
        { path: '/user', redirect: '/user/login' },//redirect, avoid rendering only ﹐ layout.js
        { path: '/user/login', component: './user/login' },
        {
          path: '/manager', component: '../pages/management/_layout.js',
          routes: [
            { path: '/manager/system', component: '../pages/management/manager/system', Routes: ['./routes/PrivateRoute.js'] }
        }
      ],
    },
  ],
};
3. Authority routing

Route implements permission route through Routes attribute, as above: / manager/system route. Create a. / routes/PrivateRoute.js permission file. The route information of / manager/system will be passed in props.

export default (props) => {
  return (
    <div>
      <div>PrivateRoute (routes/PrivateRoute.js)</div>
      { props.children }
    </div>
  );
}
4. Jump route

link mode

  import Link from 'umi/link';
  <Link to="/list">Go to list page</Link> 

router mode

  import router from 'umi/router';
  router.push({
    pathname: '/list',
    query: {
      a: 'b',
    },
  });

models level

models/index.js, cannot be empty. Because umi/dva is based on redux, redux saga data flow scheme, the connection of dva is the connection of redux, and the model of dva is the simplified version of redux saga, which is easier to use.

import * as services from '../services';
{
  namespace: 'system',                             //models namespace, need to be globally unique
  state: {
    dataList: []
  },                                      //Data store stored by models
  reducers: {
    save(state, { payload }) {                    //Update the store and merge the old state data with the new data
      return { ...state, ...payload };
    }
  },
  effects: {
    * testFunc({ payload: params }, { call, put, select }) {   //Method of dispatch request
      const { dataList } = yield select(state => state.system); //Get state in models
      const { data } = yield call(services.testFunc, params);  //Call, which requests the interfaces and parameters in services. You can continue to add parameters later, just like the call in JavaScript
      if (data && data.code == 0) {
        const data_ = data.data.content;
        yield put({ //put, action save must be issued. This action is monitored by the reducer to update the state data
          type: 'save',                                        
          payload: {
            dataList: data_ || []
          }
        });
        return data_;                                          //Return to response, optional
      }                                                        
    },
  },
  subscriptions: {                                             //Subscription, executed when app.start() starts the project
    setup({ dispatch, history }) {
      return history.listen(({ pathname, query }) => {
        // Entering the '/ manager/system' route will initiate an effect named 'save'
        if (pathname === '/manager/system') {
					//do sth... dispatch({ type: 'save', payload: query });
				}
      })
    }
  }
}

Note: call will block. Before the end of call method call, the statement after call method cannot be executed, which makes asynchronous execution in a synchronous way. If there is no blocking, it is necessary to introduce fork and use yield fork instead of yield call.

services level

The method that requests the background interface and returns a promise object.

import request from '@utils/request';

export function rightSave(values) {
  return request(`/authirty/right/save`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(values)
  });
}

components layer

dispatch

Similar to the put method in effect, action must be triggered.

  • Used in the subscription parameter of the model.
  • In the component, in the connect ed component, dispatch can be obtained in the props of the component.
import { connect } from 'dva'; 
export default connect()(System);   
  • Interface request

Mode 1 (recommended): use dispatch to call the methods declared by effects/reducer in models,

this.props.dispatch({
  type: 'system/testFunc',                                      //type, namespace / effects method name
  payload: params,                                              //payload, parameters
}).then(res => {})

//Update state with direct assignment
this.props.dispatch({
  type: 'system/save',                                          //type, namespace / reducer method name
  payload: {
    rightDetail: {a:1}
  },                                              //payload, parameters
}).then(res => {})

//Request global model
this.props.dispatch({
  type: 'global/getOrganizationList',                           //Global namespace / global effects method name
  payload: params,                                              //payload, parameters
}).then(res => {})

Mode 2: dispatch with callback callback function as the third parameter

//Component
this.props.dispatch({
  type: 'system/testFunc',
  payload: params,
  callback: (res) => {
    if (!!res) {
      //do sth
    }
  }
});

//model medium
*testFunc({ payload, callback }, { call, put }){
  const { data } = yield call(services.rightSave, payload);
  if (data && data.code == 0) {
    !!callback && && callback(data);
  }
}

Mode 3 (less): if the model store is not needed in the component, the services request interface is directly introduced

//Component
//Using the new promise request interface, service returns the promise function, which is executed after the asynchronous request of the interface
import * as service from '../services'; 
new Promise((resolve) => {
  const ret = service.rightSave(param);
  resolve(ret);
}).then((ret) => {
  if (ret && ret.code == 200) {
    //do sth
  }
});

connect

Mode 1: mapStateToProps can be used by class inheritance components and function components

After the interface request is completed, connect is introduced into the component to get the data of models and put it into the current component props

import { connect } from 'dva'; 
function mapStateToProps(state) { //state is all the models of the project
  const { selectList } = state.system; //Get the model data state with namespace system
  const { organizationList } = state.global; //Global get model data state with namespace as global
  return {
    selectList,
    organizationList
  };
}
export default connect(mapStateToProps)(System);   //connect component
//Or direct deconstruction
export default connect(({ system, global: { organizationList } }) => ({ ...system, organizationList }))(System); 

Mode 2: es6 annotation mode is introduced, which can only be used for class inheritance components

@connect(({ system, global: {organizationList} }) => ({ 
  ...system,
  organizationList
}))
class System extends React.Component{render(){return {<></>}}}

withRouter

The function of withRouter: it is available for components without route jump, such as subcomponents that want to get the location, history, match, etc. of the route. The page with route jump defaults to the existing route information.
FAQ:

  1. The url has changed, but the page component does not refresh. What's the reason?
    In layouts/index.js, if you use connect to transmit data, you need to use umi/withRouter at a higher level
import withRouter from 'umi/withRouter';
export default withRouter(connect(mapStateToProps)(LayoutComponent));
  1. Does the global layout refresh after routing switch when connect is used?
    Use with router to package the exported react components. Pay attention to the order.
import withRouter from 'umi/withRouter';
export default withRouter(connect()(Layout));

mock

export default {
  // Supported values are Object and Array
  'GET /api/users': { users: [1, 2] },
  // Support custom functions, API reference express@4
  'POST /api/users/create': (req, res) => { res.end('OK'); },
}

Such as request interface / api/list, the data returns {users: [1, 2]}

Data flow

Finally, we summarize the data flow of dva:

  • View layer dispatch operation action – > trigger corresponding methods in model layer effect – > trigger call to initiate service layer request, obtain interface data – > trigger put to initiate reducer to process corresponding action update data – > update state in model layer – > trigger render method of view layer to re render – > page update

Attach data flow chart

Reference document

umi document: https://umijs.org/zh/guide/
dva document: https://dvajs.com/guide/

ps: if there is any mistake, please leave a message to correct it!

Published 1 original article · praised 0 · visited 4
Private letter follow

Tags: React JSON npm less

Posted on Sat, 07 Mar 2020 09:14:52 -0500 by dg