react本身是组件化的,它的初衷只是提供一个UI库,但是在ES6以及webpack的大环境下,react本身已经形成了一整套全新的技术栈:
react
: 提供视图模块
react-router
: 提供视图路由切换,配合后端路由,可以开发比较强势的单页应用。
redux
: 提供程序的数据状态管理机制。本身与react并没有直接联系,但是配合react可以更好地发挥react的作用。
webpack
: 对react的jsx语法以及es6语法进行编译、对程序进行打包等等。
如果用react + redux开发一个todo程序,实现的步骤和难度会比使用angular要麻烦得多。但是从中可以学习到一些新的思想。
redux
redux本身的学习,基本只要记住它的三个东西:action
、reducer
、dispatch
。
action
action描述的是一个最基本的动作。它返回一个对象,这个对象的属性里至少包含一个type
属性,用来描述动作的行为标识,另外的各种属性则可以作为这个动作所携带的数据变化。
比如:
const addItem = (text) => {
return {
type: 'ADD_ITEM',
text
}
}
这个函数就是一个action函数,它的动作类型是ADD_ITEM
,动作携带的数据是函数传递进来的参数text
注意: 这里的action是一个函数,这是因为redux的dispatch
风格实际上是一种函数式编程风格。
reducer
reducer
也是一个函数,它用来描述一个action动作,会给程序的state带来怎样的变化。这个函数通常用一些switch语句来描述不同的action所改变的不同state。
比如:
const Todo = (state = [], action) => {
switch (action.type): {
case: 'ADD_ITEM':
return [
...state,
{action.text}
]
break;
default:
return state;
break;
}
}
注意:
reducer
中的state一般会给一个默认值,然后就是一个原则:永远不要修改state本身的任何值。所以在上面的ADD_ITEM
中,采用了ES6的数组继承的写法[...state, v]
。如果state
的表现形式是一个对象,那么就应该采用Object.assign
的写法。
如果有多个reducer
函数,则可以通过combineReducers
函数将这一堆reducer
给组合起来。
const App = combineReducers({Todo})
dispatch
dispatch
相当于事件触发,上面的reducers
使用combineReducers
组合完毕后,就可以使用redux的store了。
let store = createStore(App);
let unsubscribe = store.subscribe(() => {console.log(store.getState())});
store.dispatch(addItem('hello redux'));
unsubscribe();
使用createStore
创建了程序的状态树,通过store
的subscribe
方法实现了sub/pub
模式,并返回一个取消订阅的函数,这里用unsbuscribe
变量接收。使用dispatch
函数来触发action,改变state的当前状态。调用store.getState
来获取程序的当前状态。直接调用unsubscribe()
来取消订阅。
注意:唯一能够改变state状态的方法,就是触发action
。这样的设计保证了程序状态的可控性和可预测性。
react-redux
react-redux
是一个配合react
使用的redux npm
模块。在redux本身的基础上,提供了一个connect
中间件,把react
的UI与redux
的数据结合了起来。
也就是说,react
将更加专注地做自己的UI组件部分,redux
则专注自己的数据部分,然后在传统的react component
组件之外,新增了一个container
组件。
react component
只做自己的UI展示与逻辑,并不关心内部的props如何变化,也不知道传入自己的值是什么(但是可以通过PropTypes
来规范传入值的数据类型)。
containers
组件则是先创建两个函数,一个是mapStateToProps
,另一个是mapDispatchToProps
,这两个方法会把redux
的state
和dispatch
函数转化成react
的props
,并且一切的数据操作都会由redux
内部去管理和实现,然后通过react-redux
的connect
方法,把redux
的数据与react
的组件结合起来。
connect(mapStateToProps, mapDispatchToProps)(App);
上述的各种操作都是以函数为单位的,最为典型的就是connect
函数,它接收两个函数作为参数,并且返回一个函数,这个返回的函数又是以函数作为参数的。所以这种函数编程的写法需要理解和习惯。毕竟JavaScript
天生就是适合函数式编程的。