teasing

The company’s own products, due to historical legacy problems, the front end has been written in a project with Java. As a result, the front end was tied to the IDEA vehicle. To see page effects, start with a few Java services. To debug a change, restart the Java service. (Hot updates do exist, but they fail intermittently, causing unexpected problems for debugging.)

Reasons for choosing React. Js

Intended to do before and after separation, the previous technical route was vue.js multi-page. Want to master some more skills, the existing product structure, a reform, liberation of the front end. (The amount of code in the product is already quite large) so I consulted the big guys, under the recommendation of Fang Shao and everyone react. js big guys. Be bold with React. Js (I’ve touched on it before, but haven’t written about it). If you just implement logic, any framework will do. After writing it, I really like the sense of control and implementability of React. Js, although I have encountered many problems. I hope this will be a reference for other developers who have similar problems.

Special thanks to

React. Js is a book about React. It’s a book about React. This book is a masterpiece of conscience. (Four times so far)

The other guy, founder, no need to talk about his skills. Mainly spiritual learning and infection. Great guy !

The body of the

Mainly record the process of going from nothing to something. Write and share, and write to yourself. Note: The content covered in this article may not be difficult, and there may be some inaccuracies.

In the early

The project structure

A proper project structure brings great pleasure to development. > Represents folders

./ SRC > Assets // static resources >font-awesome logo. PNG > Components // Place dumb components >alert >icon // Put the ICONS required by dumb components together. Easy to manage alert.js alert.less // CSS modularity, only for alert.js will not cause naming pollution... Containers // place smart components > HTTP > API // for different modules of the API, to write, convenient for multi-person development, Http.js // unified configuration of axios >utils skin.js // skin file app.js index.js registerServicework.js router.js // Pull out the route, Easy to manage and modify config-overrides. Js // webpack configuration package.jsonCopy the code

package.json

."dependencies": {
    "ajv": "^ tactical fix packs for 6.5.2." "."axios": "^ 0.18.0." "."base64-js": "^ 1.3.0"."crypto-js": "^" 3.1.9-1."file-loader": "^ 1.1.11"."prop-types": "^ 15.6.2"."react": "^ 16.4.2"."react-app-rewire-less": "^ 2.1.2"."react-dom": "^ 16.4.2"."react-modal": "^ 3.5.1 track of"."react-router-dom": "^ 4.3.1." "."react-scripts": "1.1.4"."react-app-rewired": "^ 1.5.2." "},...Copy the code
  • Why not?redux react-redux? In the back, there are some trade-offs, some love and hate.
  • Code detection? Of course,eslintI’m used to this

Webpack configuration

When the project started, create-react-app was used. Surprisingly, the difficulty in starting the project did not come from writing react. js, but from WebPack 4.0. The configuration in create-react-app is hidden. The NPM eject command exposes the configuration for configuration. Finally, I found react-app-Rewired, which enabled me to complete the configuration in an elegant way. The react-app-rewired configuration is all written in config-overrides. Js under the project root, at the same level as./ SRC. The following is my configuration and some explanations. config-overrides.js

const path = require('path')
const rewireLess = require('react-app-rewire-less');

/** * @author Itroad * @version 0.1.0 ** Cover webpack's configure * @param {object} config webpack export.module = {... } * @param { string } env production || development * @return { object } custom config */
module.exports = function override(config, env) {
  if (env === "production") {

    // File path of build
    // Resolve the file reference path problem after packaging
    // It can also be configured in package.json homepage: '.', but I like to put it together for easy management
    config.output.publicPath = '. ' + config.output.publicPath

    // For require source file outside of src/. ( remove ModuleScopePlugin )
    // config.resolve.plugins = []

    // For css module
    config.module.rules[1].oneOf[2].loader[2].options['modules'] = true
    config.module.rules[1].oneOf[2].loader[2].options['localIdentName'] = '[name]_[local]__[hash:base64:5]'

    // Set the CSS reference path for the font file
    config.module.rules[1].oneOf[3].options['publicPath'] = '.. /.. / '
    
    // Path alias
    config.resolve.alias = Object.assign({}, config.resolve.alias, {
      "@src": path.resolve("src/"),
      "@http": path.resolve("src/http"),
      "@assets": path.resolve("src/assets"),
      "@components": path.resolve("src/components"),
      "@containers": path.resolve("src/containers"),
      "@reducers": path.resolve("src/reducers"),
      "@styles": path.resolve("src/styles"),
      "@utils": path.resolve("src/utils"),
      "@static": path.join(process.cwd(), './static') // SRC external resources, the default is only in./ SRC, this article does not use, here only class examples, do not recommend})}else {

    // For require source file outside of src/. ( remove ModuleScopePlugin )
    // config.resolve.plugins = []

    // For css module
    config.module.rules[1].oneOf[2].use[1].options['modules'] = true
    config.module.rules[1].oneOf[2].use[1].options['localIdentName'] = '[name]_[local]__[hash:base64:5]'
    // config.module.rules[1].oneOf[2].exclude = [
    // path.resolve(__dirname, 'node_modules'),
    // path.resolve(__dirname, 'src/components'),
    // ]
    config.module.rules[1].oneOf.push({
      test: /\.css$/.use: ['style-loader'.'css-loader'].include: [
        path.resolve(__dirname, 'node_modules'),
        path.resolve(__dirname, 'src/components')]})// Path alias
    config.resolve.alias = Object.assign({}, config.resolve.alias, {
      "@src": path.resolve("src/"),
      "@http": path.resolve("src/http"),
      "@assets": path.resolve("src/assets"),
      "@components": path.resolve("src/components"),
      "@containers": path.resolve("src/containers"),
      "@reducers": path.resolve("src/reducers"),
      "@styles": path.resolve("src/styles"),
      "@utils": path.resolve("src/utils"),
      "@static": path.join(process.cwd(), './static')})}// To support less
  config = rewireLess(config, env);

  return config;
}
Copy the code
  • Yes, after packing,index.htmlFile path in, and.cssPath to references to external resources in a file
  • CSS modularity, so naming is not a headache (more on that later)
  • Path alias, avoid path write error, look elegant. The downside is that VS Code’s automatic path matching doesn’t work anymore

Mid –

We’ve got the project structure, the configuration. Start getting into the code. After all, this is not a tutorial, so let’s say something that I think is of some value.

Since there is no UI framework, you have to implement it yourself

Modal

import React from 'react'
import ReactDom from 'react-dom'
import font from  '@assets/font-awesome/css/font-awesome.min.css'
import style from './modal.less'

const createModal = (Component, imgSrc, ModalStyle) = > {
  let body = document.body;
  let showDom = document.createElement("div");
  // Set the basic properties
  showDom.classList.add(style.toast)
  
  body.appendChild(showDom);

  // Self-delete method
  let close = (a)= > {
      ReactDom.unmountComponentAtNode(showDom);
      body.removeChild(showDom);
  }

  if(! ModalStyle) { ModalStyle = {width: '400px'.height: '500px'}}if(ModalStyle) {
    if(parseInt(ModalStyle.width, 10)) {
      ModalStyle.width = parseInt(ModalStyle.width, 10) > (window.innerWidth - 100)? (window.innerWidth - 100) : ModalStyle.width
    } else {
      ModalStyle.width = '400px'
      console.error('createToast width property value entered incorrectly, default value used ')}if(parseInt(ModalStyle.height, 10)) {
      ModalStyle.height = parseInt(ModalStyle.height, 10) > (window.innerHeight - 100)? (window.innerHeight - 100) : ModalStyle.height
    } else {
      ModalStyle.height = '500px'
      console.error('createToast Height property value entered incorrectly, default value used ') } } ReactDom.render( <div className='ReactModal__Content' style={ModalStyle}> <div className={style.head + ' Skin - modal - head '} > < div > < img SRC = {imgSrc} Alt = "emp" / > < p > bounced title < / p > < / div > < className = {I font 'fa' + ' '+ font['fa-close']} onClick={close}></i> </div> <Component close={close}/> </div>, showDom ); } export default createModalCopy the code
  • Maybe it’s functional programming
  • Create their own nodes, flexible implantation, use up delete
  • The size is set by default, and is handled out of range according to the width and height of the web page viewable range
  • throughpropsPass the popover close method to the child component
  • One other thing, not so good as an alarm log
  • Has the website havecreatePortal()Available for use. I didn’t know it until AFTER I wrote it, so I won’t change it
  • parseInt(value, 10)To prevent' ' ' 'And values that cannot be converted to numbers

Alert

CreateConfirm createInfo createWarning createError and clearAlert clearAlert. Talk about createConfirm and nothing else.

const createConfirm = (msg, cb) = > {
 showDom = document.createElement("div");

 // Set the basic properties
 showDom.classList.add(style.toast)
 document.body.appendChild(showDom);

 // Self-delete method
 let close = (a)= > {
     ReactDom.unmountComponentAtNode(showDom);
     document.body.removeChild(showDom);
 }

 const ModalStyle = {
   width: '300px'.height: '165px'
 }

 ReactDom.render(
     <div className='ReactModal__Content' style={ModalStyle}>
       <div className={style.head + ' skin-modal-head'} >
         <div>
           <img src={confirm} alt="emp"/>
           <p>confirm</p>
         </div>
         <i className={font['fa'] + ' ' + font['fa-close']} onClick={close}></i>
       </div>
       <div className={style.confirmContent}>
         <p className={style.msg}>
           {msg}
         </p>
       </div>
       <div className={style.footer}>
         <div onClick={close}>cancel</div>
         <div onClick={cb}>determine</div>
       </div>
     </div>,
     showDom
 );
}
Copy the code

Invoke the sample


/** * confirm click the callback of the confirm button * @param {any} params */
 confirmCallBack (params) {
   console.log('test', params)
   clearAlert()
 }

 /** * Test confirm popup */
 showNewConfirm () {
   createConfirm('Are you sure you want to delete XXX? '.this.confirmCallBack.bind(this.123))}Copy the code
  • Use acbCallback to handleconfirmClick ok on the event

Toast

This is even easier

import React from 'react'
import ReactDom from 'react-dom'
import style from './toast.less'

const createToast = (text, time) = > {
  let body = document.body;
  let showDom = document.createElement("div");
  // Set the basic properties
  showDom.classList.add(style.toast)
  
  body.appendChild(showDom);

  // Self-delete method
  let close = (a)= > {
      ReactDom.unmountComponentAtNode(showDom);
      body.removeChild(showDom);
  }
  if(!parseInt(time, 10)) {
    time = 1500
  }
  setTimeout(close, time)
  ReactDom.render(
      <div className={style.content}>
        {text}
      </div>,
      showDom
  );
}

export default createToast
Copy the code
  • The second argument, passed in the shutdown time, defaults to 1500 milliseconds

The skin

Add the skin style to the page

/** * @author Jiang Yang ** @description Generates the skin style * @version 0.0.1 */

const skin = {}

skin.iceBlue = {
  // Global font color
  appColor: '#FFFFFF'.appBgColor: 'black'.// header
  headerBgColor: '#010a1c'.// Frame header background color

  // left menu
  leftMenuBgColor: '#2c3e50'.// Left menu background color
  leftMenuBorderColor: '#2c3e50'.// Left menu edge color

  // right menu
  rightMenuBgColor: '#2c3e50'.// Right menu background color
  rightMenuBorderColor: '#2c3e50'.// Right menu edge color

  // content
  contentBgColor: 'rgb(60, 71, 84)'.// Frame content part background color

  // footer
  footerBgColor: '#2c3e50'.// Frame bottom background color
  footerShadowColor: 'black'.// Frame bottom shadow color

  // modal
  modalOverlay: 'rgba (49, 52, 70, 0.75)'.// Popover mask layer
  modalContentBg: '#1f2c3a'.// Popover background
  modalContentShadow: 'gray'.// Popover shadows
  modalContentTxt: 'white'.// Popover font color
  modalHeadBg: '# 091323' // Popover header
}

skin.lightBlue = {
  // Global font color
  appColor: 'black'.appBgColor: 'white'.// header
  headerBgColor: 'blue'.// footer
  footerBgColor: 'blue'.footerShadowColor: 'black'.// left menu
  leftMenuBgColor: 'white'.leftMenuBorderColor: '#2c3e50'.// right menu
  rightMenuBgColor: 'white'.rightMenuBorderColor: '#2c3e50'.// content
  contentBgColor: 'white',}let getSkinStyle = (skin) = > {
  if(! skin) {return ' ';
  }
  return `
    .skin-app {
      color: ${skin.appColor};
      background-color: ${skin.appBgColor};
    }
    .skin-header {
      background-color: ${skin.headerBgColor};
    }
    .skin-left-menu {
      background-color: ${skin.leftMenuBgColor};
      border-right: 1px solid ${skin.leftMenuBorderColor};
    }
    .skin-right-menu {
      background-color: ${skin.rightMenuBgColor};
      border-left: 1px solid ${skin.rightMenuBorderColor};
    }
    .skin-content {
      background-color: ${skin.contentBgColor};
    }
    .skin-footer {
      background-color: ${skin.footerBgColor};
      box-shadow: 0 -1px 10px ${skin.footerShadowColor};
    }
    .ReactModal__Overlay {
      background-color: ${skin.modalOverlay}! important; } .ReactModal__Content { background-color:${skin.modalContentBg}! important; box-shadow: 0px 0px 10px${skin.modalContentShadow};
      color: ${skin.modalContentTxt};
    }
    .skin-modal-head {
      background-color: ${skin.modalHeadBg};
    }
  `
}

let setSkinStyle = (skin) = > {
  let styleText = getSkinStyle(skin);
  let oldStyle = document.getElementById('skin');
  const style = document.createElement('style');
  style.id = 'skin';
  style.type = 'text/css';
  style.innerHTML = styleText;
  oldStyle ? document.head.replaceChild(style, oldStyle) : document.head.appendChild(style);
}

setSkinStyle(skin.iceBlue)

export {skin, setSkinStyle}

Copy the code

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route } from 'react-router-dom'
import App from './App';
import Login from './components/login/login.js'
import registerServiceWorker from './registerServiceWorker';

import '@utils/skin' // we can do it here

ReactDOM.render(
    <HashRouter>
      <div style={{height:'100%'}}>
        <Route exact path='/' component={Login} />
        <Route path='/home' component={App} />
      </div>
    </HashRouter>,
  document.getElementById('root')
);
registerServiceWorker();

Copy the code
  • Define a skinlike color object, then generate a style from the object and insert it into the page
  • Multiple skins, multiple skin color objects

Menu

It wasn’t worth anything anyway. React implements it as a reference. menu.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import font from  '@assets/font-awesome/css/font-awesome.min.css'
import style from './menu.less'

class Menu extends Component {
  static propTypes = {
    data: PropTypes.array
  }

  constructor () {
    super(a)this.state = {
      isShow: null}}/** * Lifecycle hook: Update state * @param {object} nextProps */ based on changes in props
  static getDerivedStateFromProps (nextProps, prevState) {
    if (prevState.isShow) {
      return null
    }
    
    const data = nextProps.data

    / * * * recursive generates isShow object, expand and contract control menu * @ param {array | object} data * @ return {item + id: true, item2: false,... } * /
    function getIsShow (data) {
      let isShow = {}
      function getIsShowState (data) {
        if(data instanceof Array) {
          for(let item of data){
            getIsShowState(item)
          }
        } else {
          isShow['item' + data.id] = data.show
          getIsShowState(data.children)
        }
      }
      getIsShowState(data)
      return isShow
    }

    return {
      isShow: data ? getIsShow(data) : null}}@param {number} id menu id */
  handleClickMenu (id) {
    let current = {
      ['item'+ id]: !this.state.isShow['item'+ id]
    }
    let copyState = this.state.isShow
    this.setState({
      isShow: Object.assign({}, copyState, current)
    })
  }

  handleDisposeOperate (value) {
    if(this.props.operateCallBack) {
      this.props.operateCallBack(value)
    }
  }

  @param {array} data menu data * @param {number} id menu ID */
  handleCreateMenu (data, id) {
    let menuDom = [];

    if (data instanceof Array) {
      let list = []
      for (let item of data) {
        list.push(this.handleCreateMenu(item))
      }
      menuDom.push(
        <ul key={id ? 'ul-'+id : 'root'} className={style.menuUl} style={{display: id ? this.state.isShow['item'+id]? 'block' : 'none' : 'block'}} >
          {list}
        </ul>)}else {
      let levelClass = data.level === 1 ? 'levelTop' : 'levelItem'
      let margLeft = (data.level * 16) + 'px'

      menuDom.push(
        <li key={data.id} id={data.id}>
          {
            data.children.length > 0
            ? <div onClick={this.handleClickMenu.bind(this, data.id)} className={style[levelClass]} style={{'paddingLeft': margLeft}}>
                <div>
                  {
                    data.level === 1
                    ? <img className={style.icon} src={require('./icon/' + data.icon)} alt="icon"/>
                    : ''
                  }
                  <span>{data.name}</span>
                </div>
                {
                  this.state.isShow['item' + data.id]
                  ? <i className={font['fa'] + ' ' + font['fa-angle-down']}></i>
                  : <i className={font['fa'] + ' ' + font['fa-angle-right']}></i>
                }
              </div>
            :
              data.operate
              ? <div onClick={this.handleDisposeOperate.bind(this, data.operate)}>
                  <div className={style[levelClass]} style={{'paddingLeft': margLeft}}>
                    <div>
                      {
                        data.level === 1
                        ? <img className={style.icon} src={require('./icon/' + data.icon)} alt="icon"/>
                        : ''
                      }
                      <span>{data.name}</span>
                    </div>
                  </div>
                </div>
              : <Link to={data.path}>
                  <div className={style[levelClass]} style={{'paddingLeft': margLeft}}>
                    <div>
                      {
                        data.level === 1
                        ? <img className={style.icon} src={require('./icon/' + data.icon)} alt="icon"/>
                        : ''
                      }
                      <span>{data.name}</span>
                    </div>
                  </div>
                </Link>
          }
          
          {this.handleCreateMenu(data.children, data.id)}
            
        </li>
      )
    }

    return menuDom;
  }

  render () {
    return (
      <div className=''>
        {this.props.data ? this.handleCreateMenu(this.props.data) : ''}
      </div>
    )
  }
}

export default Menu
Copy the code
  • handleCreateMenu()Based on the data, the menu structure is generated
  • About this structure, the official website also mentioned, to havekeyThat’s why it’s passed in, rightidAnd also to control menu folding
  • Menu folding, usestateTo control. Let’s seegetIsShow()
  • I used some of thisThe ternary judgmentI even nested it

Part of the life cycle will be obsolete

Although I have to read the official website for this, I was also delayed in reading the React book due to life cycle problems.

  • First of all, provide the official website description, Chinese
  • componentWillMount()I don’t have to do that, so the logic before the mount, I writeconstructor()
  • componentWillUpdate() componentWillReceiveProps()I don’t need these two. I’ll writestatic getDerivedStateFromProps()

In the late

Found the front of the development, write about the same. Not much later.

  • I have written two documents, DOCS. Md and standard.md DOCS. Md is about the use and instructions of related plug-ins and functions. Standard.md is the code specification document for this project, partly for this project, mostly for general specifications

  • Add a file, env.js, to the interface public path, and then manually import env.js in index.html

    window.PROJECT_CONFIG = {
    production: "http://..."
    }
    Copy the code

    Put variables under window so that window.project_config. production gets the desired data in the code, and when packaged, the file still exists and can be changed. When the environment changes, you don’t have to change the code and then package it.

Why not use Redux?

This is part of the page

redux
react-redux
map...








Write in the back

The process of writing code can be grueling. But it’s good to be done. Not so confused anymore. Also added a lot of experience and knowledge point.