...

Callwoola

大雄'blog

Home 主页 Blog 博客 Tag标签 GitHub开源 about me关于我


Redux 核心理念

Redux 是一个前端的 MVC 框架, 我们可以看看这个架构模式对比图

Redux 传统后端 MVC
store 数据库实例
state 数据库中存储的数据
dispatch(action) 用户发起请求
action: { type, payload } type 表示请求的 URL,payload 表示请求的数据
reducer 路由 + 控制器(handler)
reducer中的 switch-case 分支 路由,根据 action.type 路由到对应的控制器
reducer 内部对 state 的处理 控制器对数据库进行增删改操作
reducer 返回 nextState 将修改后的记录写回数据库

看了这个图,那么我们就好解释了: redux-mindmap

一切都可以根据上表去寻找对应概念

我们再记忆一下重要的 api

两个规定

// error
var state = store.getState()
state.counter = state.counter + 1 // 禁止在业务逻辑中直接修改 state

综合例子

在线 demo

<!DOCTYPE html>
<html>
<head>
  <script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script>
</head>
<body>
<script>
// 动作生成器
// 我们可以理解成 url 请求 , 这个里面可以携带 payload 等参数
function inc() {
  return { type: 'INCREMENT' };
  // NOTE: 不过 type 属性必须的
  // 例如:
  // return {type: 'login',payload: {account: root,password: 123}}
}
function dec() {
  return { type: 'DECREMENT' };
}

// 可以理解成 Handler 路由器, 根据 type 绑定
function reducer(state, action) {
  // 首次调用本函数时设置初始 state
  state = state || { counter: 0 };

  switch (action.type) {
    case 'INCREMENT':
      return { counter: state.counter + 1 };
    case 'DECREMENT':
      return { counter: state.counter - 1 };
    default:
      return state; // 无论如何都返回一个 state
  }
}

var store = Redux.createStore(reducer);

console.log( store.getState() ); // { counter: 0 }
// store 通过 dispatch 非配动作 
store.dispatch(inc());
console.log( store.getState() ); // { counter: 1 }

store.dispatch(inc());
console.log( store.getState() ); // { counter: 2 }

store.dispatch(dec());
console.log( store.getState() ); // { counter: 1 }
</script>
</body>
</html>

看到这里 你基本对 Redux 已经有了一个比较概括的认识了!

下面我们就可以进阶了

Redux 深度探险

compose 在线 传送门

<!DOCTYPE html>
<html>
<head>
  <script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script>
</head>
<body>
<script>
function func1(num) {
  console.log('func1 获得参数 ' + num);
  return num + 1;
}

function func2(num) {
  console.log('func2 获得参数 ' + num);
  return num + 2;
}

function func3(num) {
  console.log('func3 获得参数 ' + num);
  return num + 3;
}

// 有点难看(如果函数名再长一点,那屏幕就不够宽了)
var re1 = func3(func2(func1(0)));
console.log('re1:' + re1);

console.log('===============');

// 使用 compose 后很优雅
var re2 = Redux.compose(func3, func2, func1)(0);
console.log('re2:' + re2);
</script>
</body>
</html>

输出:

func1 获得参数 0
func2 获得参数 1
func3 获得参数 3
re1:6
===============
func1 获得参数 0
func2 获得参数 1
func3 获得参数 3
re2:6

redux 异步方案

链接

可能在现实问题中很少使用的这个, 实际上 createStore 是可以面向解决异步编程问题的 这个问题我会在另一个章节里面补充

combineReducers 多 Reducer 解决方案

链接

我们这样理解, Reducer 就相当于 控制器, 在一个控制器实现所有的方案多臃肿啊

我们可以使用 combineReducers 拆分成多个控制器处理不同的逻辑, 把 state 分而治之!

好吧, 这样说可能不太好理解!

// 加入我们面对了一个这样的数据 , 是不是很复杂
// state
//   ├── counter: 0
//   ├── todos
//         ├── optTime: []
//         ├── todoList: [] # 这其实就是原来的 todos!

// 传统我要需要在 一个 reducer 里面写多少个 Case ? 很难维护
// 这样子
// reducers/
//    ├── index.js < combineReducers (生成 rootReducer)
//    ├── counterReducer.js
//    ├── todoReducers/ <-- combineReducers
//            ├── index.js
//            ├── optTimeReducer.js
//            ├── todoListReducer.js 
// 我们建立 了一多层结构的 Reducer 目录
// 这个数据 每一个 node 都能一一找到对应的 Reducer ,这样是不是很清晰

我们开看看代码:

/** 本代码块记为 code-9 **/
/* reducers/index.js */
import { combineReducers } from 'redux'
import counterReducer from './counterReducer'
import todosReducer from './todosReducer'

const rootReducer = combineReducers({
  counter: counterReducer, // <- 键名就是该 reducer 对应管理的 state
  todos: todosReducers
})

// state
//   ├── counter: 0
//   ├── todos
//         ├── optTime: []
//         ├── todoList: [] # 这其实就是原来的 todos!
export default rootReducer
// ==================================================
/* reducers/counterReducer.js */
export default function counterReducer(counter = 0, action) { // 传入的 state 其实是 state.counter
  switch (action.type) {
    case 'INCREMENT':
      return counter + 1 // counter 是值传递,因此可以直接返回一个值
    default:
      return counter
  }
}
// ==================================================
// todos 我们继续分成两个 reducer 
// 当然你想怎么分都可以, 也可以不分
// 也可以无限分, 根据实际需求看着办吧~
/* reducers/todosReducers/optTimeReducer.js */
export default function optTimeReducer(optTime = [], action) {
  // 咦?这里怎么没有 switch-case 分支?谁说 reducer 就一定包含 switch-case 分支的?
  return action.type.includes('TODO') ? [ ...optTime, new Date() ] : optTime
}

/* reducers/todosReducers/todoListReducer.js */
export default function todoListReducer(todoList = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [ ...todoList, action.payload ]
    default:
      return todoList
  }
}

bindActionCreators

link

这个我就不说了,大家直接看链接内容吧

中间件

直接看代码

<!DOCTYPE html>
<html>
<head>
  <script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script>
</head>
<body>
<script>
function inc() {
  return { type: 'INCREMENT' };
}
function dec() {
  return { type: 'DECREMENT' };
}
function reducer(state,action) {
  state = state || { counter: 0 };
  switch (action.type) {
    case 'INCREMENT':
      return { counter: state.counter + 1 };
    case 'DECREMENT':
      return { counter: state.counter - 1 };
    default:
      return state;
  }
}

function printStateMiddleware(middlewareAPI) { // 记为【锚点-1】,中间件内可用的 API
  return function (dispatch) {                 // 记为【锚点-2】,传入原 dispatch 的引用
    return function (action) {
      console.log("dispatch 前:", middlewareAPI.getState());
      var returnValue = dispatch(action);  // 还记得吗,dispatch 的返回值其实还是 action
      console.log("dispatch 前:", middlewareAPI.getState(), '\n');
      return returnValue; // 继续传给下一个中间件作为参数 action
    };
  };
}

// 另一种写法 (与上面的方法二选一)
const printStateMiddleware = ({ getState }) => next => action => {
  console.log("dispatch 前:", getState());
  let returnValue = next(action);
  console.log("dispatch 前:", getState());
  return returnValue;
}}

// 创建 Store 前注册一个中间件
var enhancedCreateStore = Redux.applyMiddleware(printStateMiddleware)(Redux.createStore);
var store = enhancedCreateStore(reducer);

store.dispatch(inc());
store.dispatch(inc());
store.dispatch(dec());
</script>
</body>
</html>

result

dispatch 前:{ counter: 0 }
dispatch 后:{ counter: 1 }

dispatch 前:{ counter: 1 }
dispatch 后:{ counter: 2 }

dispatch 前:{ counter: 2 }
dispatch 后:{ counter: 1 }

总结

Referrence:

莞式教育 -> https://github.com/kenberkeley/redux-simple-tutorial
莞式教育 -> Redux 源码分析

  • 文档信息:
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 发表日期: 2016-11-1320:46:35+0800
  • 更多内容:
  • Feed订阅:
相关内容:

disqus评论区:

0