第3章 从Flux到Redux

3.1 Flux

单向数据流,React是用来替换Jquery的,Flux是以替换Backbone.js、Ember.js等MVC框架为主的。

actionTypes.js定义action类型;

actions.js定义action构造函数,决定了这个功能模块可以接受的动作;

reducer.js定义这个功能模块如何响应action.js中定义的动作;

views目录,包含这个功能模块中所有的React组件,包括傻瓜组件和容器组件;

index.js这个文件把所有的角色导入,然后统一导出。

一个Flux应用包含四个部分:

Dispatcher,处理动作分发,维持Stror之间的关系;

Store,负责存储数据和处理数据相关逻辑;

Action,驱动dispatcher和JavaScript对象;

View,视图部分,负责显示用户界面。

如果非要把Flux 和MVC 做一个结构对比,那么, Flux 的Dispatcher 相当于MVC 的Controller, Flux 的Store 相当于MVC 的Model, Flux 的View 当然就对应MVC 的View了,至于多出来的这个Action ,可以理解为对应给MVC 框架的用户请求。

MVC最大的缺点就是无法禁绝View和Model之间的直接对话。

Flux应用实例:

1、Dispatcher

  首先创造一个Dispatcher,Dispatcher的作用就是派发action,几乎所有应用都只需拥有一个Dispathcer,在src下创造一个唯一的Dispatcher对象

import {Dispatcher} from 'flux'

export default new Dispacther()

2、Action

  代表一个动作,一个纯粹的数据对象。

  action对象必须有一个名为type的字段,代表这个action对象的类型,为了记录日志和debug方便,这个type应该是字符串类型。

  定义action通常需要两个文件,一个定义action类型,一个定义actio的构造函数(也称为action creator)。原因是store会根据action不同类型做不同操作。

  在src/Actiontypes.js中,定义action的类型

//两个action类型 INCREMENT和DECREMENT,一个是点击+按扭,一个是点击-按扭
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

  在src/Action.js中,定义action的构造函数,这里边定义的并不是action对象本身,而是能够产生并派发action对象的函数。

//引入ActionTypes和AppDispatcher,直接使用Dispatcher
import * as ActionTypes from './ActionTypes'
import AppDispatcher from './AppDispatcher'

//Action.js导出了两个action的构造函数increment和decrement,当这两个构造函数被调用的时候,创造了对象的action对象,并立即通过AppDispatcher.dispatch函数派发出去。
export const increment = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
})
} export const decrement = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
})
}

3、Store

  一个Store也是一个对象,这个对象存储应用状态,同时还要接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态。

  当Store的状态发生变化时,需要通知应用的其它部分做出响应,做出响应的当然是view部分,但是我们硬编码这种联系,应该用消息的方式建立Store和View的联系。这就是为什么让CounterStore 扩展成了EventEmitter.proptype,等于让CounterStore成了一个EventEmitter对象。

一个EventEmitter的实例对象支持下列相关函数:

emit函数,可以广播一个特定事件,第一个参数是字符串类型的事件名称;

on函数,可以增加一个挂在这个EventEmitter对象特定事件上的处理函数,第一个参数是字符串类型的事件名称,第二个参数是处理函数;

removeListener函数,和on函数做的事情相反,删除挂在这个EventEmitter对象特定事件上的处理函数,参数一样。

import AppDispatcher from '../AppDispatcher'
import * as ActionTypes from '../ActionTypes'
import {EventEmitter} from 'events'
import { Action } from 'rxjs/internal/scheduler/Action'; const CHANGE_EVENT = 'change' const counterValues = {
'First': 0,
'Second': 10,
'Third': 20
} const CounterStore = Object.assign({},EventEmitter.prototype,{
//让应用的其它模块可以读取当前的计数值
getCounterValues:function(){
return counterValues
},
//对CounterStore状态更新的广播
emitChange:function(){
this.emit(CHANGE_EVENT)
},
//添加监听函数
addChangeListener:function(callback){
this.on(CHANGE_EVENT, callback)
},
//删除监听函数
removeChangeListener:function(callback){
this.removeListener(CHANGE_EVENT, callback)
}
})

把CounterStore注册到全局唯一的Dispatcher上去。Dispatcher有一个函数叫register,接受一个回调函数作为参数。返回值是一个token,这个token用于Sotre之前的同步,在CounterStore中暂时用不到。

//把register返回值保存在CounterStore对应的dispatchToken字段上
CounterStore.dispatchToken = AppDispatcher.register((action)=>{
if(action.type === ActionTypes.INCREMENT){
counterValues[action.counterCaption] ++;
CounterStore.emitChange()
}else if(action.type === ActionTypes.DECREMENT){
counterValues[action.counterCaption] --;
CounterStore.emitChange()
}
}) export default CounterStore

 Flux的核心:当通register函数把一个回调函数注册到Dispatcher之后,所有派发给Dispatcher的action对象,都会传递到这个回调函数中来。

import React, {Component} from 'react'
import Counter from './Counter' class ControlPanel extends Component{ render() {
return (
<div>
<Counter caption='First' />
<Counter caption='Second' />
<Counter caption='Third' />
</div>
)
}
} export default ControlPanel

  

import React, {Component} from 'react'
import * as Actions from '../Action'
import CounterStore from '../stores/CounterStore'
const buttonStyle = {
margin: '10px'
}
class Counter extends Component{
constructor(props){
super(props)
// console.log('enter constructor',props.caption) this.add = this.add.bind(this)
this.math = this.math.bind(this)
this.onChange = this.onChange.bind(this)
this.state={
count: CounterStore.getCounterValues()[props.caption]
}
} shouldComponentUpdate(nextProps, nextState){
return (nextProps.caption !== this.state.caption ||
nextProps.count !== this.state.count)
} componentDidMount(){
CounterStore.addChangeListener(this.onChange)
} componentWillUnmount(){
CounterStore.removeAllListeners(this.onChange)
} onChange(){
const newCount = CounterStore.getCounterValues()[this.props.caption]
this.setState({count: newCount})
} add(){
Actions.increment(this.props.caption)
} math() {
Actions.decrement(this.props.caption)
} render(){
console.log('enter render', this.props.caption)
return(
<div>
<button style={buttonStyle} onClick={this.add}>+</button>
<button style={buttonStyle} onClick={this.math}>-</button>
<span>count:{this.state.count}</span>
{/* <span>props:{this.props}</span> */}
</div>
)
}
}
Counter.defaultProps={
initValue: 0,
onUpdate: f => f //默认是一个什么都不做的函数
} export default Counter

4、View

存在于flux框架中的React组件需要实现以下几个功能:

创建时要读取Store上状态来初始化组件内部状态;

当Store上状态发生变化时,组件要立刻同步更新内部保持状态一致;

View如果要改变Store状态,必须并且只能派发action。

5、Flux的不足

  1.Store之间的依赖关系

  在Flux体系中,如果两个Store之间有逻辑关系,就必须用上Dispatcher的waitFor函数。

  2.难以进行服务器渲染

  3.Store混杂了逻辑和状态

  Store封装了数据和处理数据的逻辑,当我们需要替换一个Store的逻辑时,只能把整个Store整体替换掉,无法保持Store中的存储状态。

3.2 Redux

Redux的三个基本原则:

唯一数据源(Single Source of Truth);

保持状态只读(State is read-only);

数据改变只能通过纯函数完成(Changes are made with pure functions)。

1、唯一数据源

  唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。

  Flux是状态数据分散在多个Store中,容易造成数据冗余。

  Redux就是整个应用只有一个Store,所有组件的数据源就是这个Store上的状态。

  这个唯一Store上的状态,是一个树的形象。

2、保持状态只读

  不能去直接修改状态,要修改Store的状态,必须要通过派发一个action对象完成,这一点和flux没区别。

3、数据改变只能通过纯函数完成

  纯函数就是Reducer,Redux就是Reducer+Flux。

3.2.2 Redux实例

ActionTypes.js

export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

Action.js

import * as ActionTypes from './ActionTypes'
export const increment = (counterCaption) => {
//Redux返回一个action 对象
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
}
} export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
}
}

  

Store.js

这个文件输出全局唯一的Store

import {createStore} from 'redux'
import reducer from './Reducer' const initValues = {
'First' : 0,
'Secont' : 10,
'Third' : 20
}
//第一个参数代表更新状态,第二个参数代表初始状态,第三个参数Store Enhancer,暂时用不上
const store = createStore(reducer, initValues) export default store

  

Reducer.js

和Flux应用中注册的回调函数一样,与Flux不同的是多了一个state,Flux中没有,是因为state是由Store管理而不是由Flux。

Redux把存储state的工作抽取出来交给Redux框架本身,让reducer只关心如何更新state,而不管state怎么存。

import * as ActionTypes from './ActionTypes'

export default (state, action) => {
const {counterCaption} = action
/**
* return {...state,[counterCaption] : state[counterCaption] + 1}等同于
* const newState = Object.assign({},state)
* newState[counterCaption] ++
* return newState
*/
switch (action.type) {
case ActionTypes.INCREMENT:
//...操作符,表示把state所有字段扩展开
return {...state,[counterCaption] : state[counterCaption] + 1}
case ActionTypes.DECREMENT:
return { ...state, [counterCaption]: state[counterCaption] - 1 }
default:
return state;
}
}

 ControlPanel.js

import React, {Component} from 'react'
import Counter from './Counter'
import SumCounter from './SumCounter' class ControlPanel extends Component{ render() {
return (
<div>
<Counter caption='First' />
<Counter caption='Second' />
<Counter caption='Third' />
<SumCounter />
</div>
)
}
} export default ControlPanel

 Counter.js

import React, {Component} from 'react'
import * as Actions from '../Action'
import store from '../Store'
const buttonStyle = {
margin: '10px'
}
class Counter extends Component{
constructor(props){
super(props)
// console.log('enter constructor',props.caption) this.add = this.add.bind(this)
this.math = this.math.bind(this)
this.getOwnState = this.getOwnState.bind(this)
this.onChange = this.onChange.bind(this)
this.state = this.getOwnState()
} getOwnState(){
// console.log(store.getState(),'state')
return {
value: store.getState()[this.props.caption]
}
} componentDidMount(){
//通过Store的subscribe监听其变化,只要Store的状态发生变化就会调用这个组件的onChange方法。
//增加监听的函数也可以写到构造函数中
store.subscribe(this.onChange)
}
componentWillUnmount(){
//把监听注销
store.unsubscribe(this.onChange)
}
onChange(){
this.setState(this.getOwnState())
} add(){
//派发action
//action构造函数只负责创建对象,要派发action就需要调用store.dispatch函数
store.dispatch(Actions.increment(this.props.caption))
// Actions.increment(this.props.caption)
} math() {
store.dispatch(Actions.decrement(this.props.caption))
// Actions.decrement(this.props.caption)
} render(){
const value = this.state.value
const {caption} = this.props
return(
<div>
<button style={buttonStyle} onClick={this.add}>+</button>
<button style={buttonStyle} onClick={this.math}>-</button>
<span>{caption} count:{value}</span>
</div>
)
}
} export default Counter

  SumCounter.js

import React, {Component} from 'react'
import store from '../Store' class SumCounter extends Component{
constructor(props){
super(props) this.onChange = this.onChange.bind(this)
this.getOwnState = this.getOwnState.bind(this)
store.subscribe(this.onChange)
this.state = this.getOwnState() } onChange(){
this.setState(this.getOwnState())
} componentWillUnmount(){
store.unsubscribe(this.onChange)
}
getOwnState(){
const state = store.getState()
let sum = 0
for(const key in state){
if(state.hasOwnProperty(key)){
sum += state[key]
}
}
return {sum}
}
render(){
return(
<div>Total: {this.state.sum}</div>
)
}
} export default SumCounter

3.2.3 容器组件和傻瓜组件

在Redux框架下,一个React组件需要完成两个功能:

  1.和Redux Store打交道,读取Store状态,用于初始化组件的状态,同时还要监听Store的状态改变;当Store发生改变时,需要更新组件状态,驱动组件重新渲染;当需要更新

  Store状态时,就要派发action对象;

  2.根据当前props和state,渲染出用户界面。

  为了让一个组件只专注做一件事,可以把这个组件拆分成多个组件,让每个组件只专注做一件事。

  上面也说了在Redux框架下,一个React组件南非要完成两个功能,可以考虑拆分成两个组件,把两个组件嵌套起来,完成原本一个组件完成的所有任务。

  两个组件是父子关系。承担第一个任务的组件,也就是负责和Redux Store打交道的组件,处于外层,所以被称为容器组件(Container Component),又叫聪明组件;对于承担第二个任务的组件,也就是只专心负责渲染页面的组件,处于内层,叫做展示组件,又叫傻瓜组件。

  傻瓜组件就是一个纯函数,根据props产生的结果。

Counter.js拆分

import React, {Component} from 'react'
import * as Actions from '../Action'
import store from '../Store'
const buttonStyle = {
margin: '10px'
}
/*
* 傻瓜组件
* Counter组件完全没有state,只有一个render方法,所有的数据都来自props,这种组件叫“无状态”组件
*/
// class Counter extends Component{
// render(){
// const {caption, add, math, value} = this.props
// return(
// <div>
// <button style={buttonStyle} onClick={add}>+</button>
// <button style={buttonStyle} onClick={math}>-</button>
// <span>{caption} count:{value}</span>
// </div>
// )
// }
// } //缩减版傻瓜组件,无状态组件,因为没有状态,不需要对象表示,所以连类都不需要了,对于只有一个render方法的组件,缩略为一个函数足矣。
function Counter({ caption, add, math, value }){
//解构赋值或者props参数
// function Counter(props) {
// const { caption, add, math, value } = props
return (
<div>
<button style={buttonStyle} onClick={add}>+</button>
<button style={buttonStyle} onClick={math}>-</button>
<span>{caption} count:{value}</span>
</div>
)
} //容器组件
/**
* CounterContainer承担了所有的和Store关联的工作,它的render函数所做的就是渲染傻瓜组件Counter而已,只负责传递必要的prop
*/
class CounterContainer extends Component{
constructor(props){
super(props)
// console.log('enter constructor',props.caption) this.add = this.add.bind(this)
this.math = this.math.bind(this)
this.getOwnState = this.getOwnState.bind(this)
this.onChange = this.onChange.bind(this)
this.state = this.getOwnState()
} getOwnState(){
// console.log(store.getState(),'state')
return {
value: store.getState()[this.props.caption]
}
} componentDidMount(){
//通过Store的subscribe监听其变化,只要Store的状态发生变化就会调用这个组件的onChange方法。
//增加监听的函数也可以写到构造函数中
store.subscribe(this.onChange)
}
componentWillUnmount(){
//把监听注销
store.unsubscribe(this.onChange)
}
onChange(){
this.setState(this.getOwnState())
} add(){
//派发action
//action构造函数只负责创建对象,要派发action就需要调用store.dispatch函数
store.dispatch(Actions.increment(this.props.caption))
// Actions.increment(this.props.caption)
} math() {
store.dispatch(Actions.decrement(this.props.caption))
// Actions.decrement(this.props.caption)
} render(){
const value = this.state.value
const {caption} = this.props
return(
<Counter caption={caption} add={this.add} math={this.math} value={value} />
)
}
} //export导出的是容器组件,对于这个视图模块来说,根本不会感受到傻瓜组件的存在,从外部看到的就只是容器组件。
export default CounterContainer

3.2.4 组件Context

不能每一个组件用到Store都去引入一次,需要定义一个全局的,只有一个地方需要导入Store,这个位置应该在调用最顶层的React的位置。

Context就是“上下文环境”,让一个树状组件上的所有组件都能访问一个共同的对象。

首先上级要宣称自己支持context,并且提供一个函数来返回代表Context的对象。

然后,这个上级组件下的所有子孙组件,只需要宣称自己需要这个context,就可以通过this.context访问到这个共同的环境对象。

定义一个Provider.js组件

import {Component} from 'react'
import PropTypes from 'prop-types' /**
* Provider也是一个React组件,它的render函数就是简单的把子组件渲染出来,在渲染上,Provider不做作何附加的事情。
*/
class Provider extends Component{
//这个函数返回的就是代表Context的对象
//要求Provider的使用者通过prop传递进来store
getChildContext(){
return {
store: this.props.store
}
}
render(){
/**
* 每个React组件的props中都可以有一个特殊属性children,代表的是子组件
* this.props.children就是两个Provider标签之间的<ControlPanel />
* <Provider>
* <ControlPanel />
* </Provider>
*/
//把渲染工作交给子组件
return this.props.children
}
}
//为了让Provider能够被React认可为一个Context的提供者,还需要指定Provider的childContextTypes属性
//类的childContextTypes,必须和getChildContext对应,只有两者都齐备,Provider的子组件才有可能访问到context.
Provider.childContextTypes = {
store: PropTypes.object
} export default Provider

入口文件index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Provider from './redux_width_context/Provider'
import store from './redux_width_context/Store' import ControlPanel from './redux_width_context/views/ControlPanel' ReactDOM.render(
<Provider store={store}>
<ControlPanel />
</Provider>
, document.getElementById('root'))

ControlPanel.js

import React, {Component} from 'react'
import Counter from './Counter'
import SumCounter from './SumCounter' class ControlPanel extends Component{
render(){
return(
<div>
<Counter caption='First' />
<Counter caption='Second' />
<Counter caption='Third' />
<SumCounter />
</div>
)
}
} export default ControlPanel

 Counter.js

import React, {Component} from 'react'
import * as Actions from '../Action'
// import store from '../Store'
import PropTypes from 'prop-types' const buttonStyle={
margin: '10px'
}
function Counter(props){
const {cpation, add, math, value} = props
return (
<div>
<button style={buttonStyle} onClick={add}>+</button>
<button style={buttonStyle} onClick={math}>-</button>
<span>{cpation} Count: {value}</span>
</div>
)
} class CounterContainer extends Component{
//因为定义了自己的构造函数,所以要用上第二个参数context
constructor(props, context){
console.log(context, context)
//super的时候要带上context,这样才能上React组件初始化实例中的context,不然组件的其它部分就无法使用this.context
//也可以用...arguments的方法
super(props,context)
this.getOwnState = this.getOwnState.bind(this)
this.add = this.add.bind(this)
this.math = this.math.bind(this)
this.onChange = this.onChange.bind(this)
this.context.store.subscribe(this.onChange)
this.state=this.getOwnState()
} getOwnState(){
return {
// value: store.getState()[this.props.caption]
//所有的store访问者是通过this.context.store完成,this.context就是Provider提供的context对象
value: this.context.store.getState()[this.props.caption]
}
} componentWillUnmount(){
this.context.store.unsubscribe(this.onChange)
} onChange(){
this.setState(this.getOwnState())
} add(){
this.context.store.dispatch(Actions.increment(this.props.caption))
} math(){
this.context.store.dispatch(Actions.decrement(this.props.caption))
} render(){
const value = this.state.value
return( <Counter caption={this.props} add={this.add} math={this.math} value={value} /> )
}
}
/**
* 给CounterContainer类的contextTypes赋值和Provider.childContextTypes一样的值,两者必须一致,不然无法访问到context
*/
CounterContainer.contextTypes = {
store: PropTypes.object
}
export default CounterContainer

3.2.5 React-Redux

  React应用改进的两个方法,

  第一是把一个组件拆分为容器组件和傻瓜组件,第二是使用React的Context来提供一个所有组件都可以直接访问的Context。

  React-Redux就是把这两种方法的套路部分抽取出来复用,这样每个组件的开发只需关注不同的部分就可以了。

React-Redux和Redux的不同就是react-redux不再使用自己实现的Provider,而是从react-redux库导入Provider,

react-redux的两个最主要功能:

  connect:连接容器组件和傻瓜组件

  Provider:提供包含store的context。

1、connect函数具体做的工作:

  把Store上的状态转化为内层傻瓜组件的prop;

  把内层傻瓜组件中的用户动作转化为派送给Store的动作。

  一个是内层傻瓜对象的输入,一个是内层傻瓜对象的输出。

Counter.js

import React, {Component} from 'react'
import * as Actions from '../Action'
// import store from '../Store'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'; const buttonStyle={
margin: '10px'
}
function Counter(props){
const {cpation, onIncrement, onDecrement, value} = props
return (
<div>
<button style={buttonStyle} onClick={onIncrement}>+</button>
<button style={buttonStyle} onClick={onDecrement}>-</button>
<span>{cpation} Count: {value}</span>
</div>
)
} /**
* 把Store上的状态转化为内层组件的Props
* @param {*} state
* @param {*} ownProps
*/
function mapStateToProps(state, ownProps){
return {
value: state[ownProps.caption]
}
} /**
* 把内层傻瓜组件中用户动作转化为派送给Store的动作,也就是把内层傻瓜组件暴露出来的函数类型的prop关联上dispatch函数的调用,
* 每个prop代表的回调函数的主要区别就是dispatch函数的参数不同。
* @param {*} dispatch
* @param {*} ownProps
*/
function mapDispatchToProps(dispatch, ownProps){
return {
onIncrement: () => {
dispatch(Actions.increment(ownProps.caption))
},
onDecrement: () => {
dispatch(Actions.decrement(ownProps.caption))
}
}
} /**
* 给CounterContainer类的contextTypes赋值和Provider.childContextTypes一样的值,两者必须一致,不然无法访问到context
*/
CounterContainer.contextTypes = {
store: PropTypes.object
}
/**
* connect是react-redux提供的一个方法
* 接收两个参数,执行结果依然是一个函数,
* 这里有两个函数执行:
* 第一次是connect函数的执行;
* 第二次把Connect函数执行的结果再执行,这一次的参数是Counter傻瓜组件,最后产生的就是容器组件。
*/
export default connect(mapStateToProps, mapDispatchToProps)(Counter)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// import Provider from './redux_width_context/Provider'
import {Provider} from 'react-redux'
import store from './redux_width_context/Store'
import ControlPanel from './react_redux/views/ControlPanel' ReactDOM.render(
<Provider store={store}>
<ControlPanel />
</Provider>
, document.getElementById('root'))

  

2、Provider

react-redux要求store不光是一个object,而且必须是包含三个函数的object,这三个函数分别是:

subscribe、dispatch、getState

  react-redux定义了Provider的componentWillReceiveProps函数,在React组件的生命周期中,componentWillReceiveProps函数在每次重新渲染时都会调用到,react-redux在componentWillReceiveProps函数中会检查这一次渲染时代表store的prop和上一次的是否一样。如果不一样,就会给出警告,必免多次渲染了不同的Redux Store。

  每个Redux应用都只能有一个Redux Store,在整个Redux的生命周期中都应保持Store的唯一性。

第3章 从Flux到Redux的更多相关文章

  1. 【转】浅谈React、Flux 与 Redux

    本文转自<浅谈React.Flux 与 Redux>,转载请注明出处. React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化 利用 props 形成 ...

  2. Vuex、Flux、Redux、Redux-saga、Dva、MobX

    https://www.jqhtml.com/23003.html 这篇文章试着聊明白这一堆看起来挺复杂的东西.在聊之前,大家要始终记得一句话:一切前端概念,都是纸老虎. 不管是Vue,还是 Reac ...

  3. 状态管理(Vuex、 Flux、Redux、The Elm Architecture)

    1.https://vuex.vuejs.org/zh-cn/intro.html (vuex) 这就是 Vuex 背后的基本思想,借鉴了 Flux.Redux.和 The Elm Architect ...

  4. 从Flux到Redux详解单项数据流

    从Flux到Redux是状态管理工具的演变过程,但两者还是有细微的区别的.但是最核心的都还是观察者模式的应用. 一.Flux 1. Flux的处理逻辑 通俗来讲,应用的状态被放到了store中,组件是 ...

  5. 第四章 模块化React和Redux应用

    第四章 模块化React和Redux应用 4.1 模块化应用要点 构建一个应用的基础: 代码文件的组织结构: 确定模块的边界: Store的状态树设计. 4.2 代码文件的组织方式 4.2.1 按角色 ...

  6. 数据管理工具Flux、Redux、Vuex的区别

    目录 为什么要进行数据管理? 怎么有效地进行数据管理? 数据管理工具 1. Flux 2. Redux 3. Vuex 使用数据管理工具的场景 相关资料 主要讲解一下前端为什么需要进行数据管理,有效的 ...

  7. 状态管理之 Flux、Redux、Vuex、MobX(概念篇)

    本文是对 Flux.Redux.Vuex.MobX 几种常用状态管理模式的总结,偏向于概念层面,不涉及过多代码. 状态管理 什么是状态管理? 状态管理就是,把组件之间需要共享的状态抽取出来,遵循特定的 ...

  8. 理顺react,flux,redux这些概念的关系

    作者:北溟小鱼hk链接:https://www.zhihu.com/question/47686258/answer/107209140来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转 ...

  9. 浅谈 React、Flux 与 Redux

    React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化利用 props 形成单向的数据流根据 state 的变化来更新 view利用虚拟 DOM 来提升渲染性能 ...

随机推荐

  1. 后台发送http请求通用方法,包括get和post

    package com.examsafety.service.sh; import java.io.BufferedReader; import java.io.IOException; import ...

  2. eventkeyboardmouse

    http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml

  3. 学习MAP 地图好地址

    http://www.cnblogs.com/beniao/archive/2010/01/13/1646446.html Bēniaǒ成长笔记在IT江湖里我不是一名老手,我只是一名普通的程序员,愿意 ...

  4. 玲珑学院OJ 1028 - Bob and Alice are playing numbers 字典树,dp

    http://www.ifrog.cc/acm/problem/1028 题解处:http://www.ifrog.cc/acm/solution/4 #include <cstdio> ...

  5. 【OI新闻】2016.10.06

    今天有人说好多OJ都狗记邓了- 翻了一下,恭喜以下OJ赢得大奖,获得狗记邓徽章一枚 一等奖Codevs 二等奖Bzoj 三等奖洛谷 后记-感悟 如果正在为OJ发愁的朋友,不要悲伤,不要心急,换一换OJ ...

  6. android获取手机的IMSI码

    android--获取手机的IMSI码,并判断是中国移动\中国联通\中国电信转载 TelephonyManager telManager = (TelephonyManager) getSystemS ...

  7. 牛客网9.9比赛 C 保护

    题目大意: n个城市构成一个树 m支军队 每只军队守卫 在xi到yi的最短路径上的城市 q个重要人物从vi出发 找到离根最近的点使从vi到这个点上所有路径都可以被至少ki个军队完全覆盖 输出每个答案的 ...

  8. Java IO 输入输出流 详解 (一)***

    首先看个图: 这是Javaio 比较基本的一些处理流,除此之外我们还会提到一些比较深入的基于io的处理类,比如console类,SteamTokenzier,Externalizable接口,Seri ...

  9. vue商品详情页添加动画(eg)

    <template> <div class="food" transition="move"></div> </tem ...

  10. bzoj 1575: [Usaco2009 Jan]气象牛Baric【dp】

    完了不会dp了 设f[i][j]为以i结尾,有j个时的最优值,辅助数组g[i][j]为s选了i和j,i~j中的误差值 转移是f[j][i]=min(f[k][i-1]+g[k][j]) #includ ...