Web 应用是一个状态机,视图与状态是一一对应的。 所有的状态,保存在一个对象里面。 在组件化的应用中(比如react、vue2.0等),会有着大量的组件层级关系,深嵌套的组件与浅层父组件进行数据交互,变得十分繁琐困难。而redux,站在一个服务级别的角度,可以毫无阻碍地(这个得益于react的context机制,后面会讲解)将应用的状态传递到每一个层级的组件中。redux就相当于整个应用的管家。
1、单一数据源 : 整个应用状态,都应该被存储在单一store的对象树中。 2、只读状态: 唯一可以修改状态的方式,就是发送(dispatch)一个动作(Action),通俗来讲,就是说只有getter,没有setter。 3、使用纯函数去修改状态
(1)Action Action是唯一可以改变状态的途径,服务器的各种推送、用户自己做的一些操作,最终都会转换成一个个的Action,而且这些Action会按顺序执行,这种简单化的方法用起来非常的方便。Action 是一个对象。其中的type属性是必须的,表示 Action 的名称:
const action = { type: 'home', msg: 'Write Document' };(2)Store Store管理着整个应用的状态,Store提供了一个方法dispatch,这个就是用来发送一个动作,去修改Store里面的状态,然后可以通过getState方法来重新获得最新的状态,也就是state。 (3)Reducer 当dispatch之后,getState的状态发生了改变,Reducer就是用来修改状态的。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
const reducer = function (state, action) { // ... return new_state; };知道这些基本概念,我们来手动封装一个redux试试。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button onclick="store.dispatch({type:'COUNT_DECREMENT',num:1})">-</button> <h1 id="count"></h1> <button onclick="store.dispatch({type:'COUNT_INCREMENT',num:1})">+</button> <script> //集中管理state,和dispatch,action const createStore = (reducer) => { let state = null; const getState = () => state; //定义一个监听器队列,管理所有订阅的方法 const listeners = []; const subscribe = (listener) => listeners.push(listener) const dispatch = (action) => { state = reducer(state, action) listeners.forEach((listener) => listener()) } dispatch({}) return { getState, dispatch, subscribe } } //默认的初始状态 const countState = { count: 0 } //每次返回一个新的状态, redux 里面称为 reducer const changeState = (state, action) => { if (!state) return countState; switch (action.type) { case "COUNT_DECREMENT": return { count: state.count - action.num } case "COUNT_INCREMENT": return { count: state.count + action.num } default: return state } } const store = createStore(changeState); // 定义一个方法用于渲染计数器的dom const render = () => { document.getElementById('count').innerHTML = store.getState().count; } // 首次渲染数据 render(); // 监听,只要有dispatch,render就会自动运行 store.subscribe(render) </script> </body> </html>我们要知道的是 redux 不是一定要依赖react的执行环境的。那怎么在react中用redux
// ----------------------------------------------首先要创建仓库 import {createStore} from 'redux' // 初始化的状态 const initialState = { name: 'zhangsan', } // 操作状态的方法 const reducer = (state = initialState, action)=>{ // 修改state的时候,需要返回一个新的对象。 // 如果不需要修改,直接返回原来的state // 这里对 action 中的type 属性进行 处理,分别执行不同的代码,但有一点,都要返回state。 } // 仓库 const store = createStore(reducer); export default store; //--------------------------然后在组件中引用 import React, { PureComponent } from "react"; import store from "../store"; class Add extends PureComponent { constructor(props) { super(props); // 将全局仓库中的状态转为了组件的状态,通过store.getState().value 来获取到数据 this.state = { value: store.getState().value } } render() { // 接收数据 const { value } = this.state; return ( <div className="add"> <button onClick={this.addAction}>新增</button> </div> ); addAction(){ //想要更改数据的话,要调用store中的dispatch方法,穿入一个action,action中的两个参数,第一个是 事件类型 type, //第二个是携带的参数 store.dispatch({type:""add",value:"lisi"}) // 一旦dispatch 一个 action store 中就会接受响应。判断action 中type 的类型。 } //这样数据就可以修改,但是页面并不知道数据已经修改了。所以要进行一个监听 componentDidMount(){ // 监听仓库中,数据的变化 this.unsubscribe = store.subscribe(()=>{ // 接收全局中新的状态转为组件的状态,组件会更新。 this.setState({value: store.getState().value}); }) } componentWillUnmount(){ // 移除监听 this.unsubscribe(); } }这样一个简单的redux 就在react中实现了。
如果要分模块来管理,就要生成不同的仓库。该怎么做。
import { createStore, combineReducers } from 'redux' import user from './modules/user' import cart from './modules/cart' // 将多个reducer合并为一个reducer,提供给store,分模块使用 const reducer = combineReducers({ user: user, cart, }); const store = createStore(reducer); export default store;获取值的时候,就要加上一个模块名字的前缀了。
如果安装了redux的插件的话(redux devtools),加上下面一段代码,就可以在浏览器中用插件来查看redux的数据流了。
import { compose } from 'redux' // 判断浏览器是否安装了redux devtools插件 const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(reducer, composeEnhancers());