reactredux前端开发实战 (react与redux开发实例精解)

react+redux整合开发流程

一、前言

1.1 准备工作

(1) 安装node

react与redux开发实例精解,reactredux前端开发实战

(2) 安装git

react与redux开发实例精解,reactredux前端开发实战

(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与redux开发实例精解,reactredux前端开发实战

而对于react而言,它自身更强调自己位V-view视图层。同时自身采用单向数据流的思想,此时Flux或者redux状态库管理库就配合react就变得非常完美。

react与redux开发实例精解,reactredux前端开发实战

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) 目录结构

react与redux开发实例精解,reactredux前端开发实战

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文件

//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

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文件

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文件

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文件

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文件

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作为容器组件

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作为容器组件

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文件

import React, {

Component

} from ’react’;

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开发实例精解,reactredux前端开发实战

react与redux开发实例精解,reactredux前端开发实战

五、总结

本文介绍了react+redux基本概念和思想。同时对于react+redux的整合开发流程做了比较详细的介绍,结合代码以及代码注释阐述了react+redux的思想。就目前前端技术vue、angular、react都各自的思想,个人对于react的思想非常倾佩,vue和angular的双向绑定(MVVC)也非常热衷。希望本文能够对读者有帮助