本帖最后由 小蜀哥哥 于 2018-10-25 17:07 编辑
react+redux整合开发流程
一、前言
1.1 准备工作
(1) 安装node
(2) 安装git
(3) 安装一款前端IDE --- 本人使用HBuilder
1.2 预备知识
(1) nodejs基本命令
(2) 前端html+javascript+css基础知识
(3) react16相关概念和基本使用
(4) JSX语法
(5) ES6语法
二、react介绍
2.1 Virtual DOM
虚拟DOM是React的基石:(1) 引入虚拟DOM主要是解决Web页面大量操作DOM的性能问题
(2) 在React中,应用程序在虚拟DOM上操作,这让React有了优化的机会
(3) 提供开发服务端应用、Web应用和手机端应用等平台一直的开发方式
2.2 React组件
(1) 所谓组件,即封装起来的具有独立功能的UI部件,React并不是MVC的前端框架 对于React而言,则完全是一个新的思路,开发者从功能的角度出发,将UI分成不同的组件,每个组件都独立封装
(2) 组件化开发特性: 1.可组合:一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部.通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件 2.可重用:每个组件都是具有独立功能的,它可以被使用在多个UI场景 3.可维护:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护 4.可测试:因为每个组件都是独立的,那么对于各个组件分别测试显然要比对于整个UI进行测试容易的多 2.3 Jsx语法
Jsx语法是将HTML语言直接写在JavaScript语言之中,不加任何引号。它允许 HTML 与 JavaScript的混写。 JSX的特点: 1.类XML语法容易接受,结构清晰 2.增强JS语义 3.抽象程度高,屏蔽DOM操作,跨平台 4.代码模块化,组件化,使得每一个组件维护自己的UI 2.4 单向数据流
React是单向数据流,数据主要从父节点传递到子节点(通过props),如果顶层(父级)的某个props改变了,React会重渲染所有的子节点。
三、redux介绍
3.1 传统MVC
传统MVC强调分层开发,model(M)-模型层,view(V)-视图层,controller(C)-控制层,在传统MVC框架中,通常使用双向绑定的方式来将Model的数据展现到View。当Model中的数据发生变化时,一个或多个View会发生变化;当View接受了用户输入时,Model中的数据则会发生变化。如下图所示, Model 和 View 之间的关系错综复杂,导致出现问题时很难调试;实现新功能时也需要时刻注意代码是否会产生副作用
file:////tmp/wps-zhongpeihuan/ksohtml/wpsHGPRZl.jpg
而对于react而言,它自身更强调自己位V-view视图层。同时自身采用单向数据流的思想,此时Flux或者redux状态库管理库就配合react就变得非常完美。
redux流程的逻辑非常清晰,数据流是单向循环的,就像一个生产的流水线: store(存放状态) -> container(显示状态) -> reducer (处理动作)-> store 3.2 Redux三个概念
1.store:是应用的状态管理中心,保存着是应用的状态(state),当收到状态的更新时,会触发视觉组件进行更新。
2.container:是视觉组件的容器,负责把传入的状态变量渲染成视觉组件,在浏览器显示出来。
3.reducer:是动作(action)的处理中心, 负责处理各种动作并产生新的状态(state),返回给store。
四、react+redux整合开发
4.1 环境搭建(1) 安装create-react-app
[size=12.0000pt] npm install --global create-react-app (2) 创建项目 create-react-app react-redux-demo(3) 在当前工程目录下添加redux依赖 npm install redux --save
npm install react-redux --save(4) 目录结构 file:////tmp/wps-zhongpeihuan/ksohtml/wpsKHBLez.jpg
目录介绍:
- public 静态资源存放目录
- img 图片文件夹
- plugins 第三方插件文件夹
- index.html 工程入口访问页面
- src 源码文件夹:包含项目源码,我们基本都在这个文件夹下做开发
- containers 容器文件夹:存放容器组件
- components 组件文件夹:存放普通显示组件
- actions actions文件夹:存放可以发出的action
- reducers reducers文件夹:存放action的处理器reducers
- contants 定义全局常量- app.js react整合redux数据流入口
- index.js react应用访问入口
4.2 流程描述
react: 作为视图层,通过dispatcher发送action去触发reducer中的方法来获取redux中store中得状态或者数据进行展示。
redux:
(1) 定义action,通过设定action的type属性和参数
(2) 编写处理器reducer,reducer接收action,根据action的type属性执行相关业务并返回新的state。
(3) 在容器组件里面讲react和redux进行连接。主要是将redux的state和dispatcher绑定到react组件的prop属性下,这样就可以进行向下传递,完成单向数据流
注:store作为redux的数据存储,可以看作数据库。store里面存在很多state,对于一个reducer一般都处理一个state.
4.3 代码实现
1. 创建项目Action常量,在src/contants目录创建actionTypes.js文件
[JavaScript] 纯文本查看 复制代码 //action方法名和actionType常量
export const ADD_BOOK = 'ADD_BOOK'; //添加书籍操作
export const DELETE_BOOK = 'DELETE_BOOK';//删除书籍操作
export const QUERY_BOOK = 'QUERY_BOOK';//查询书籍操作
export const CHECK_ADD = 'CHECK_ADD';//选择删除的记录操作
export const CHECK_REMOVE = 'CHECK_REMOVE';//取消删除操作
export const CHECK_CLEAR = 'CHECK_ALL';//全选
export const CHECK_CLEAR = 'CHECK_CLEAR';//取消全选
2. 创建项目action函数,在src/actions目录下创建action.js
[JavaScript] 纯文本查看 复制代码 import { ADD_BOOK, DELETE_BOOK, QUERY_BOOK,CHECK_ADD,CHECK_REMOVE,CHECK_CLEAR,CHECK_ALL } from "../constants/actionTypes"
/**
* receiveData和fetchData是绑定的,fetchData是异步action去获取public目录下的data.json
* 然后发送dispatcher去执行receiveData,最好返回异步加载的data数据
*/
export const receiveData = data => ({ type: 'RECEIVE_DATA', data: data });
export const fetchData = () => {
return dispatch => {
fetch('/data.json')
.then(res => res.json())
.then(json => dispatch(receiveData(json)));
};
};
/**
* addBook新增数据action
*/
export function addBook(book) {
return {
type: ADD_BOOK,
book
}
}
/**
* deleteBook根据ids删除数据
*/
export function deleteBook(ids) {
return {
type: DELETE_BOOK,
ids
}
}
/**
* queryBook根据书籍名称查询列表
*/
export function queryBook(bookName) {
return {
type: QUERY_BOOK,
bookName
}
}
/**
* checkAdd选择删除的记录id,添加到待删除数组列表
*/
export function checkAdd(id) {
return {
type: CHECK_ADD,
id
}
}
/**
* checkRemove从待删除数组列表中取消删除的记录id
*/
export function checkRemove(id) {
return {
type: CHECK_REMOVE,
id
}
}
/**
* checkClear将待删除数组列表清空
*/
export function checkClear() {
return {
type: CHECK_REMOVE
}
}
/**
* 将记录id全部添加到待删除列表
*/
export function checkAll(ids) {
return {
type: CHECK_ALL,
ids
}
}
3. 创建项目reducers函数
规则:每一个reducer应该维护一种状态或者数据。每一个reducer都是一个独立的js文件。在本案例中存在查询条件、数据列表、待删除数组
3.1) 创建维护待删除数组列表state的js文件,在reducers/bookDeleteState.js文件
[JavaScript] 纯文本查看 复制代码 import { CHECK_ADD ,CHECK_REMOVE,CHECK_CLEAR,CHECK_ALL} from "../constants/actionTypes"
/**
* 待删除列表state
* CHECK_ADD:执行添加待删除记录
* CHECK_REMOVE:执行移除待删除记录
* CHECK_ALL:全选进入待删除记录
* CHECK_CLEAR:清空待删除记录
*/
export default function bookDeleteState(state = [], action) {
switch(action.type) {
case CHECK_ADD:
return [...state,action.id];
case CHECK_REMOVE:{
let state_= [];
let j=0;
for(var i=0;i<state.length;i++){
if(state[i] != action.id){
state_[j++]=state[i]
}
}
return state_; }
case CHECK_ALL:
return [...state,action.ids];
case CHECK_CLEAR:
return [];
default:
return state;
}
}
3.2) 创建维护查询条件state的js文件,在reducers/bookQueryState.js文件[JavaScript] 纯文本查看 复制代码 import { QUERY_BOOK } from "../constants/actionTypes"
/**
* 维护查询条件的state
* QUERY_BOOK:设置查询条件
*/
export default function bookQueryState(state = "", action) {
switch(action.type) {
case QUERY_BOOK:
return action.bookName;
default:
return state;
}
} 3.3) 创建数据列表state的js文件,在reducers/bookListState.js文件[JavaScript] 纯文本查看 复制代码 import { ADD_BOOK, DELETE_BOOK } from "../constants/actionTypes"
/**
* 维护数据列表的State
* ADD_BOOK:执行新增书籍操作
* DELETE_BOOK:执行删除书籍的操作
* RECEIVE_DATA:异步请求,返回查询到的列表
*/
export default function bookListState(state = [], action) {
switch(action.type) {
case ADD_BOOK:
return [...state, action.book];
case DELETE_BOOK:{
let state_= [];
let j=0;
for(var i=0;i<state.length;i++){
if(action.ids.indexOf(state.id) == -1){
state_[j++]=state
}
}
return state_;
}
case "RECEIVE_DATA":
return action.data;
default:
return state;
}
} 3.4) 将redux的reducer合并,并把它们的结果合并成一个state对象,在reducers/reducer.js文件[JavaScript] 纯文本查看 复制代码 import { combineReducers } from 'redux';
import bookListState from './bookListState';
import bookQueryState from './bookQueryState';
import bookDeleteState from './bookDeleteState';
const rootReducer = combineReducers({
bookListState,
bookQueryState,
bookDeleteState
});
export default rootReducer; 3.5) 将使用容器组件将react和redux连接在一起,绑定state数据和dispatcher到react组件的prop 注意:这里将App作为容器组件,因为这里只有一个模块,但是实际情况中存在多个模块,会采用路由的方式进行不同模块的单页跳转,所以应该每一个模块定义一个顶层的容器组件,而不是采用App.js作为容器组件[JavaScript] 纯文本查看 复制代码 import React, { Component } from 'react';
import { addBook, queryBook,deleteBook,checkAdd,checkRemove,checkClear,checkAll,fetchData } from "./actions/action"
import { connect } from 'react-redux';
import BookItem from "./components/BookItem";
import logo from './logo.svg';
//App作为容器组件
class App extends Component {
constructor(props) {
super(props);
this.saveBook = this.saveBook.bind(this);
this.queryBook_ = this.queryBook_.bind(this);
}
componentDidMount() {
this.props.searchData();
}
saveBook(){
const book = {
id:this.id.value,
name:this.name.value,
author:this.author.value,
price:this.price.value
};
this.props.saveBook(book)
}
queryBook_(name){
if(name == "ALL"){
this.props.queryBook("");
}else{
this.props.queryBook(this.search.value);
}
}
render() {
let { dispatch,bookList,checkIds,checkAdd,checkRemove,deleteBook } = this.props;
return (
....
<form className="navbar-form navbar-left">
<div className="form-group">
<input type="text" ref={(search)=>{this.search = search}} className="form-control" placeholder="Search" />
</div>
<button type="button" className="btn btn-default" onClick={this.queryBook_}>Submit</button>
</form>
....
<button type="button" onClick={()=>{ this.id.value="";this.name.value="";this.author.value="";this.price.value="" }} className="btn btn-default" data-toggle="modal" data-target="#myModal">新增</button>
<button type="button" className="btn btn-default" onClick={this.queryBook_.bind(this,"ALL")}>刷新</button>
<button type="button" className="btn btn-default" onClick={()=>{ deleteBook(checkIds) }}>删除</button>
....
<table className="table table-striped">
<thead>
<th></th>
<th>#</th>
<th>书名</th>
<th>作者</th>
<th>价格</th>
</thead>
<tbody>
{ bookList.map(item => <BookItem item={item} checkAdd={checkAdd} checkRemove={checkRemove}/>) }
</tbody>
</table>
....
<form className="form-horizontal" role="form">
<input type="text" ref={(id) => {this.id = id}} className="form-control" id="id" placeholder="请输入id" />
<input type="text" ref={(name) => {this.name = name}} className="form-control" id="lastname" placeholder="请输入书名" />
<input type="text" ref={(author) => {this.author = author}} className="form-control" id="lastname" placeholder="请输入作者" />
<input type="number" ref={(price) => {this.price = price}} className="form-control" id="lastname" placeholder="请输入价格" />
<button type="button" className="btn btn-primary" data-dismiss="modal" onClick={this.saveBook}>提交保存</button>
</form>
....
);
}
}
//条件过滤
const bookFilter = (bookList ,filter)=>{
console.log(filter);
return bookList.filter(item => item.name.indexOf(filter)!=-1);
}
//绑定state数据到容器组件的prop属性
const mapStateToProps = (state) => {
return {
//state.bookListState 数据列表
//state.bookQueryState 过滤条件
bookList: bookFilter(state.bookListState,state.bookQueryState),
checkIds: state.bookDeleteState
}
};
//绑定dispatcher到容器组件的prop属性
const mapDispatchToProps = (dispatch,ownProps) => {
return {
searchData:()=>{
dispatch(fetchData());
},
saveBook:(book)=>{
dispatch(addBook(book));
},
queryBook:(value)=>{
dispatch(queryBook(value));
},
deleteBook:(ids)=>{
dispatch(deleteBook(ids));
},
checkAdd: (id) => {
dispatch(checkAdd(id));
},
checkRemove: (id) => {
dispatch(checkRemove(id+""));
},
checkClear: () => {
dispatch(checkClear());
},
checkAll: () => {
dispatch(checkAll());
}
};
}
//将绑定后的prop属性连接到容器组件
export default connect(mapStateToProps,mapDispatchToProps)(App); 3.6) 创建普通组件,创建components/BookItem.js文件[JavaScript] 纯文本查看 复制代码 import React, {
Component
} from 'react';
class BookItem extends Component {
constructor(props) {
super(props);
this.checkID=this.checkID.bind(this);
}
checkID(event){
var flag = event.target.checked;
if(flag){
this.props.checkAdd(event.target.value);
}else{
this.props.checkRemove(event.target.value);
}
}
render() {
let {item} = this.props;
return(
<tr>
<td><input type="checkbox" value={item.id} onClick={this.checkID}/></td>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.author}</td>
<td>{item.price}</td>
</tr>
)
};
}
export default BookItem; 4.4 运行
在dos窗口进入当前工程下执行如下命令启动项目 npm start 效果图[size=12.0000pt]
file:////tmp/wps-zhongpeihuan/ksohtml/wpsqz0E2Y.jpg[size=12.0000pt]
五、总结
本文介绍了react+redux基本概念和思想。同时对于react+redux的整合开发流程做了比较详细的介绍,结合代码以及代码注释阐述了react+redux的思想。就目前前端技术vue、angular、react都各自的思想,个人对于react的思想非常倾佩,vue和angular的双向绑定(MVVC)也非常热衷。希望本文能够对读者有帮助。
|