foreword
Recently, the company has come to a few interns, and I happen to have nothing important at hand, and then the leader asked me to take them to learn react and lay the foundation for the next react project.
Then I wrote a few demos at random to help them understand how a serious project builds a configuration project.
Share it now, I hope it can help those in need. Although some directories in this demo are useless, I still list them out in order to show the directory skeleton structure of a regular project.
I have not classified the create-react-app template files. After understanding, you can classify them yourself and add a style folder.
text
As far as the current environment is concerned, when developing react or vue projects, there should be few people who drink react library files directly in html, and they all use the front-end construction tool webpack + es6 to write projects. Therefore, I directly recommend the scaffolding create-react-app officially produced by facebook to do the project. create-react-app is the most suitable scaffolding for beginners to learn to use, all are simple commands. There is no need to clone the entire project first, and then install the dependencies like other scaffolding.
The first step (install the tool, generate the project, start the project)
Install the create-react-app tool
npm install -g create-react-app
build project
create-react-app react-demo
After the project is generated, all dependencies will be downloaded automatically.
After entering the project, start the project
cd react-demo
npm start 或者 yarn start
Both npm and yarn are package managers, and both can download dependencies. The details are not explained here. You can search for details by yourself. A project is best to use a package management tool, do not mix, use yarn to download dependencies below. Do not do otherwise .
After the project starts, open port 3000.
The project started successfully as shown below:
The second step (configure react-router4.0, including routing parameters, etc.)
1. Install dependencies
yarn add react-router-dom --save
2. Create a new file and configure routing
src
Create a new directory in the containers
directory Place the main page file in
src
the directory Create a new directory in the components
directory Place a high-reuse template file
in containers
the directory Create a new one inHome.js
Home.js code
import React, { Component } from 'react';
class Home extends Component {
render() {
return (
<div>
Home
</div>
);
}
}
export default Home;
containers
New inList.js
List.js code
import React, { Component } from 'react';
class List extends Component {
render() {
return (
<div>
List
</div>
);
}
}
export default List;
containers
New inMine.js
Mine.js code (secondary routing, routing parameter example)
import React, {Component} from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import Mine1 from './Mine1'
import Mine2 from './Mine2'
class Mine extends Component {
constructor(props) {
super(props);
this.state = {
param: ''
};
}
changeParam(event) {
this.setState({
param: event.target.value
})
}
render() {
return (
<Router>
<div>
Mine
传参示例:<input type="text" placeholder='请输入要传递到mine2的参数' value={this.state.param} onChange={this.changeParam.bind(this)}/>
<div className="mine-nav">
{/*编写导航*/}
<ul>
<li><Link to={`${this.props.match.url}/mine1`}>Mine1</Link></li>
<li><Link to={`${this.props.match.url}/mine2/${this.state.param? this.state.param : "default-param"}`}>Mine2</Link></li>
{/*传参示例,如果没有参数,传默认参数*/}
</ul>
{/*路由匹配*/}
<div>
<Route exact path={`${this.props.match.url}/mine1`} component={Mine1}/>
<Route path={`${this.props.match.url}/mine2/:param`} component={Mine2}/>
</div>
</div>
</div>
</Router>
);
}
}
export default Mine;
containers
New inMine1.js
Mine1.js code
import React, { Component } from 'react';
class Mine1 extends Component {
render() {
return (
<div>
Mine1
</div>
);
}
}
export default Mine1;
containers
New inMine2.js
Mine2.js code (example of routing parameters)
import React, { Component } from 'react';
class Mine2 extends Component {
render() {
return (
<div>
2222222222222 传递的参数:{this.props.match.params.param}
</div>
);
}
}
export default Mine2;
Modify the entry App.js file as follows:
import React, { Component } from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import List from './containers/List.js'
import Home from './containers/Home.js'
import Mine from './containers/Mine.js'
import './App.css'
import logo from './logo.svg'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2 className='App-title'>Welcome to React Plan</h2>
</div>
<div className="App-content">
{/*路由配置*/}
<Router>
<div className="content-box">
{/*编写导航*/}
<ul className="nav">
<li><Link to="/">首页</Link></li>
<li><Link to="/list">列表页</Link></li>
<li><Link to="/mine/mine1">我的页面二级路由</Link></li>
{/*link指向二级路由的默认页面*/}
</ul>
{/*路由匹配*/}
<div className="content">
<Route exact path="/" component={Home}/>
<Route path="/list" component={List}/>
<Route path="/mine" component={Mine}/>
</div>
</div>
</Router>
</div>
</div>
);
}
}
export default App
Modify the App.css file for a little more styling:
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/*下面是新添加的样式*/
* {
margin: 0;
padding: 0;
list-style: none;
}
.content-box{
overflow: hidden;
}
.nav {
padding: 20px;
float: left;
width: 160px;
background-color: #fcc;
}
.nav li{
line-height: 30px;
}
.content{
padding: 20px 0 0 200px;
background-color: #ccf;
height: 110px;
}
.mine-nav{
background-color: #cfc;
margin-top: 20px;
height: 70px;
}
.mine-nav ul{
overflow: hidden;
}
.mine-nav ul li{
float: left;
width: 50%;
}
.mine-nav>div{
margin-top: 20px;
}
You're done, just give it a try. After the configuration is complete, the page is as follows:
The third step (configure redux)
What is redux specifically, I won't mention it here. If you want to know more about it, it is best to read the blog of Mr. Ruan Yifeng. Redux introductory tutorial to understand what redux is, or whether your project needs redux or not. Here is only how to configure redux.
1. Install dependencies
yarn add redux react-redux --save
2. Create a new file, configure
src
the new store
directory place the store configuration file
src
in the directory, create a new reducers
directory in the directory, place regular files in
src
the directory, create a new constants
directory , place data in
src
the directory, create a new actions
directory in the directory, and place the trigger update file
in store
the new directory.configStore.js
store > configStore.js file
import { createStore } from 'redux'
//引入规则文件
import rootReducer from '../reducers/index.js'
export default function configStore(initState){
// 创建store
const store = createStore(
rootReducer,
initState,
// 如果安装了redux插件可按照版本打开下面的注释
// window.devToolsExtension ? window.devToolsExtension() : undefined
// window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : undefined
)
return store
}
reducers
New in New inindex.js
New inreducers
number.js
reducers > index.js file
// 规则性文件
import { combineReducers } from 'redux'
import number from './number'
// 如果有多个规则,则引入多个文件
//import XXXX from './XXXX'
// 创建规则
export default combineReducers({
// XXXX,
number
})
reducers > number.js file
import * as actionTypes from '../constants/number.js'
import { combineReducers } from 'redux'
const text = function (state = actionTypes.TEXT, action){
switch(action.type) {
case actionTypes.TEXT :
return action.data.text
default:
return state
}
}
const count = function (state = actionTypes.COUNT, action){
switch(action.type) {
case actionTypes.COUNT :
return action.data.count
default:
return state
}
}
export default combineReducers({
text,
count
})
constants
New innumber.js
constants > number.js file
export const COUNT = 100;
export const TEXT = 'number大类下的数据';
containers
In the modification Home.js
(slightly changed the home component to use redux)
containers > Home.js file
import React, { Component } from 'react';
import {connect} from 'react-redux';
class Home extends Component {
render() {
return (
<div>
Home <br/>
{this.props.text} ==> {this.props.count}
</div>
);
}
}
// 如果只是读取数据,那么只需要这一个方法即可。(这里两个写出来只是为了方便理解)
function mapStateToProps(state){
return {
count: state.number.count,
text: state.number.text
}
}
// 同理,如果只是设置数据,那么只需要这一个方法即可。
function mapDispatchToProps(dispatch) {
return {
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Home)
At this point, the first step should have been achieved, and the data can be read. As shown in the figure:
The read data is realized, and the setting data must be realized next.
containers
New inTestRedux.js
containers > TestRedux.js file
import React, { Component } from 'react';
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as handlerNumberActions from '../actions/number.js'
class TestRedux extends Component {
textChange(event){
this.props.handlerNumberActions.changeText({
text: event.target.value
})
}
numberAdd(){
this.props.handlerNumberActions.addNumber({
count: this.props.count
})
}
numberSubtract(){
this.props.handlerNumberActions.subtractNumber({
count: this.props.count
})
}
render() {
return (
<div style={{height:'100px',background: '#ffc',padding: '10px'}}>
<ul>
<li>修改redux数据</li>
<li>修改文本: <input type="text" style={{padding: '5px 10px'}} value={this.props.text} onChange={this.textChange.bind(this)} /></li>
<li>操作数值:
<button style={{padding: '5px 10px'}} onClick={this.numberAdd.bind(this)}>+</button>
-------------------
<button style={{padding: '5px 10px'}} onClick={this.numberSubtract.bind(this)}>-</button>
</li>
</ul>
</div>
);
}
}
function mapStateToProps(state){
return {
count: state.number.count,
text: state.number.text
}
}
function mapDispatchToProps(dispatch) {
return {
handlerNumberActions: bindActionCreators(handlerNumberActions, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TestRedux)
actions
New innumber.js
actions > number.js file
import * as actionTypes from '../constants/number.js'
export function addNumber(data){
data.count ++;
return {
type: actionTypes.COUNT,
data
}
}
export function subtractNumber(data){
data.count --;
return {
type: actionTypes.COUNT,
data
}
}
export function changeText(data){
return {
type: actionTypes.TEXT,
data
}
}
At this point, the complete redux is completely configured. Click a little, or try changing the text.
The directory structure and effect are as follows:
Step 4 (Configure fetch)
what is fetch? Fetch is actually a substitute for XMLHttpRequest. Compared with the rough things like XMLHttpRequest, fetch obviously looks more refined. The best thing is that fetch is based on Promise, which frees us from the nightmare of callback hell. Let's simply encapsulate fetch. Easy to use in react.
1. Install dependencies
yarn add whatwg-fetch es6-promise --save
2. Create a new file to encapsulate the fetch method
src
Create a new directory under the directory, create a new package fetch under fetch
the fetch
fileindex.js
fetch > index.js file
import 'whatwg-fetch'
import 'es6-promise'
export function get(url) {
let result = fetch(url, {
credentials: 'include',
headers: {
'Access-Control-Allow-Origin': '*',
'Accept': 'application/json, text/plain, */*'
},
// 设置允许cors跨域
mode: 'cors'
});
return result;
}
// 将对象拼接成 key1=val1&key2=val2&key3=val3 的字符串形式
function obj2params(obj) {
let result = '';
let item;
for (item in obj) {
result += '&' + item + '=' + encodeURIComponent(obj[item]);
}
if (result) {
result = result.slice(1);
}
return result;
}
// 发送 post 请求
export function post(url, paramsObj) {
let result = fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: obj2params(paramsObj)
});
return result;
}
Create a public
new mock
directory under the directory place the simulation data.
Create a new file in the directorymock
.list.json
public > mock > list.json
{
"errorNo": 0,
"message": "success",
"data": [
{
"name": "Kelli",
"age": 12
},
{
"name": "Vivien",
"age": 17
},
{
"name": "Jacklyn",
"age": 19
},
{
"name": "Bobbie",
"age": 32
}
]
}
Modify the file src
> .containers
List.js
src > containers > List.js
import React, {Component} from 'react';
import {get} from '../fetch/index';
class List extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() {
this.getListData()
}
getListData() {
get("./mock/list.json").then((res) => {
return res.json();
}).then((json)=>{
this.setState({
dataList: json.data
})
}).catch(function (err) {
console.log(err);
})
}
render() {
let _this = this;
function createListDom() {
return {
__html: _this.state.dataList && _this.state.dataList.map( item => {
return '<li>name: '+ item.name + ',age: '+ item.age +'</li>'
}).join('')
};
}
return (
<div>
List <br/>
<ul dangerouslySetInnerHTML={createListDom()} />
</div>
);
}
}
export default List;
This is just a simple test of the get method. As for the post request, anyone who is interested can test it in private.
The configuration is completed as shown below:
Step 5 (Configure sass)
In the high version of create-react-app, the function of supporting sass has been removed. If we still want to use it and can only configure it ourselves, we need to change the configuration file.
1. Install dependencies
yarn add node-sass sass-loader --save-dev
2. Modify the configuration
Find the filenode_modules/react-scripts/config
under , first add it in the back according to the rules , and then configure the sass file rules in .webpack.config.dev.js
exclude
/.scss$/
loaders
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader']
}
After changing the configuration of the two places, sass is configured, as shown in the figure:
webpack.config.dev.js
It is the configuration file of the development environment. If you want to take effect in the production environment, you arewebpack.config.prod.js
doing the same configuration modification.
At this point, all the configuration is complete. The pro-test of the project can be run.
Author HoChine
April 27, 2018
Project demo: http://hochine.cn/demo/react-demo
GitHub address: https://github.com/HoChine/react-demo