第3章 从Flux到Redux
第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的更多相关文章
- 【转】浅谈React、Flux 与 Redux
本文转自<浅谈React.Flux 与 Redux>,转载请注明出处. React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化 利用 props 形成 ...
- Vuex、Flux、Redux、Redux-saga、Dva、MobX
https://www.jqhtml.com/23003.html 这篇文章试着聊明白这一堆看起来挺复杂的东西.在聊之前,大家要始终记得一句话:一切前端概念,都是纸老虎. 不管是Vue,还是 Reac ...
- 状态管理(Vuex、 Flux、Redux、The Elm Architecture)
1.https://vuex.vuejs.org/zh-cn/intro.html (vuex) 这就是 Vuex 背后的基本思想,借鉴了 Flux.Redux.和 The Elm Architect ...
- 从Flux到Redux详解单项数据流
从Flux到Redux是状态管理工具的演变过程,但两者还是有细微的区别的.但是最核心的都还是观察者模式的应用. 一.Flux 1. Flux的处理逻辑 通俗来讲,应用的状态被放到了store中,组件是 ...
- 第四章 模块化React和Redux应用
第四章 模块化React和Redux应用 4.1 模块化应用要点 构建一个应用的基础: 代码文件的组织结构: 确定模块的边界: Store的状态树设计. 4.2 代码文件的组织方式 4.2.1 按角色 ...
- 数据管理工具Flux、Redux、Vuex的区别
目录 为什么要进行数据管理? 怎么有效地进行数据管理? 数据管理工具 1. Flux 2. Redux 3. Vuex 使用数据管理工具的场景 相关资料 主要讲解一下前端为什么需要进行数据管理,有效的 ...
- 状态管理之 Flux、Redux、Vuex、MobX(概念篇)
本文是对 Flux.Redux.Vuex.MobX 几种常用状态管理模式的总结,偏向于概念层面,不涉及过多代码. 状态管理 什么是状态管理? 状态管理就是,把组件之间需要共享的状态抽取出来,遵循特定的 ...
- 理顺react,flux,redux这些概念的关系
作者:北溟小鱼hk链接:https://www.zhihu.com/question/47686258/answer/107209140来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转 ...
- 浅谈 React、Flux 与 Redux
React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化利用 props 形成单向的数据流根据 state 的变化来更新 view利用虚拟 DOM 来提升渲染性能 ...
随机推荐
- 后台发送http请求通用方法,包括get和post
package com.examsafety.service.sh; import java.io.BufferedReader; import java.io.IOException; import ...
- eventkeyboardmouse
http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml
- 学习MAP 地图好地址
http://www.cnblogs.com/beniao/archive/2010/01/13/1646446.html Bēniaǒ成长笔记在IT江湖里我不是一名老手,我只是一名普通的程序员,愿意 ...
- 玲珑学院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> ...
- 【OI新闻】2016.10.06
今天有人说好多OJ都狗记邓了- 翻了一下,恭喜以下OJ赢得大奖,获得狗记邓徽章一枚 一等奖Codevs 二等奖Bzoj 三等奖洛谷 后记-感悟 如果正在为OJ发愁的朋友,不要悲伤,不要心急,换一换OJ ...
- android获取手机的IMSI码
android--获取手机的IMSI码,并判断是中国移动\中国联通\中国电信转载 TelephonyManager telManager = (TelephonyManager) getSystemS ...
- 牛客网9.9比赛 C 保护
题目大意: n个城市构成一个树 m支军队 每只军队守卫 在xi到yi的最短路径上的城市 q个重要人物从vi出发 找到离根最近的点使从vi到这个点上所有路径都可以被至少ki个军队完全覆盖 输出每个答案的 ...
- Java IO 输入输出流 详解 (一)***
首先看个图: 这是Javaio 比较基本的一些处理流,除此之外我们还会提到一些比较深入的基于io的处理类,比如console类,SteamTokenzier,Externalizable接口,Seri ...
- vue商品详情页添加动画(eg)
<template> <div class="food" transition="move"></div> </tem ...
- 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 ...