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,
eslint
I’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.html
File path in, and.css
Path 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
- through
props
Pass the popover close method to the child component - One other thing, not so good as an alarm log
- Has the website have
createPortal()
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 a
cb
Callback to handleconfirm
Click 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 have
key
That’s why it’s passed in, rightid
And also to control menu folding - Menu folding, use
state
To control. Let’s seegetIsShow()
- I used some of this
The ternary judgment
I 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.