美文网首页
从0开始的Redux旅程(一)

从0开始的Redux旅程(一)

作者: Renderz_干了这碗口嚼酒 | 来源:发表于2017-05-27 02:28 被阅读36次

    前置知识点

    • 深拷贝与浅拷贝
    • FLUX架构

    数据库模型

    无论是MVC,MVVM,还是现如今的Flux,最强调的一点就是模块化的分离解耦.
    因为整套系统是围绕数据运作的,所以

    • 表现是基于数据而来: 数据库的内容决定了展示的页面
    • 功能是基于数据而实现: 功能是在数据之上做修改

    最终的结果就是,:

    • 数据(store)决定了展示(view)
    • 指令(action)改变了数据(store)
    • 展示(view)绑定了指令(action)

    而具体是选择mvc, mvvm, 还是flux完全是基于那一部分东西多而决定的, 核心的理念都是相同的.

    优先数据

    界面归根结底是为了展现数据, 所以优先设计好数据模型. 再设计对这些数据模型所能实现的功能. 最后将这些功能附加到界面上.

    Redux初体验

    第一步总是做需求分析, 还是先做个todo

    需求分析

    • 界面可以展现一个todolist
    • 有个按钮可以增加todolist的内容, 增加的todo默认是未完成
    • 每条todo后面会有一个按钮可以删除该条todo
    • 每条todo后面会有一个按钮可以完成该条todo
    • todolist下面有三个按钮, 分别可以展示所有todolist, 展示完成的todolist, 展示未完成的todolist

    数据模型(store)设计

    • 数据是一个简单对象
    • 数据模型就是一个精简版的数据库
    • 数组总是很好的选择
    {
      displayMode: // mode类型在我们公司的产品里一般是存放在域表里, 由ID指定.
      todoList: [
        {
           text:  //  todo的内容
           status:  // 状态, 如果需要的话可以把status放域表, 或者直接赋值true / false
      ]
    }
    

    动作指令(action)设计

    • 动作指令就是对数据模型操作的一串指令
    • 动作指令是一个对象
    • 这个对象必须包含type属性
    {type:'ADD_TODO',payload:'Eating'}
    {type:'DEL_TODO',payload:1}
    {type:'TOGGLE',payload:2}
    {type:'CHANGE_DISPLAY_MODE',payload:1}
    

    其实就像是一堆机器伪代码

    方法服务(reducer)设计

    • 后端的常见思路就是将对数据库操作的SQL写成一个个数据库服务.
    • Redux里就是写reducer
    • reducer就是一个函数,接受两个参数,一个是state,一个是action
    • status就是当前的数据模型的一部分,比如如果我们动作是操作数据模型中的displayMode部分,那么我们就设计一个displayModeReducer,接受当前的displayMode的值,再接受一个action作为执行参数.
    • action就是指令参数,传入的就是action
    • 一个reducer只返回state的一部分,比如你对displayMode做调整,就只返回displayMode的值就行了,如果你对todoList做调整,那么就返回一个todoList的数组就好了.
    const displayModeReducer = function(state=0,action){
    //执行动作
      return state
    }
    const todoListReducer = function(state=0,action){
    //执行动作
      return state
    }
    

    合并服务(combineReducers)

    • 其实上边的动作都是纯原生js写的. 到这步才真正是Redux的API部分
    • 合并服务的目的是将所有Reducer合并进一个Reducers池中, 因为每个reducer只返回state的一部分, 所以将所有reducer拼起来必然是一个完整的state
    • 整合reducer的顺序按照store的结构顺序来就好了
    import { combineReducers } from 'Redux';
    const reducers = combineReducers({
        displayMode: displayModeReducer
        todoList: todoListReducer
    }
    )
    

    生成数据模型(store)

    • 这步也是Redux的API
    • createStore需要两个蚕食,一个是合并后的服务,一个是初始的state
    • 生成的store带有getState()方法可以获取当前state
    • subscribe()方法可以监听传来的指令,然后执行回调
    • dispatch()方法可以对store执行指令
    import { createStore } from 'Redux'
    const store = createStore(reducers,{})
    

    此时其实可以进行简单测试了

    store.subscriber(()=>{console.log('Listen to State Change) , store.getState()})
    //这样如果store监听到了指令的话,就会执行回调,返回store.getState().
    //可以尝试发一串指令过去看看会得到什么,比如
    store.dispatch({type: "CHANGE_DISPLAY_MODE", payload: 1})
    store.dispatch({type: "CHANGE_DISPLAY_MODE", payload: 2})
    store.dispatch({type: "CHANGE_DISPLAY_MODE", payload: 3})
    store.dispatch({type: "ADD_TODO", payload: "HAPPY"})
    store.dispatch({type: "ADD_TODO", payload: "EATING"})
    store.dispatch({type: "ADD_TODO", payload: "WASHING"})
    store.dispatch({type: "DEL_TODO", payload: 1})
    store.dispatch({type: "TOGGLE", payload: 1})
    //反正什么也得不到啦只会得到undefine,因为reducer没有正常返回,只返回了一堆空state
    //这样的话就可以继续去完善reducer了
    

    完善reducer

    • reducer的作用就是在收到指令action后,会重新返回一个新的state, 因为state的变化,那么界面也会相应变化.
    • 在reducer内主要不要对原始state进行突变(mutant), state部分是引用类型的值, 突变就会导致执行指令前后的state指向的实际对象被改变为相同的值, 这样会产生很多不好的影响比如无法做日志.
    • 现阶段好用的创建一个新的对象的方法, 可以用ES6的Object.assign方法, 结合ES6的展开运算符. 或者用一些其他库比如lodash或者jquery甚至immutable.js
    • 这部分建议多研究些方法, 看看lodash源码或者jq源码, ES6现在已经有许多很好用的原生对象API了, 数组更是能玩出花. 就不赘述了.
    import { combineReducers } from 'redux'
    const displayModeReducer = function (state = 0, action) {
        switch (action.type) {
            case "CHANGE_DISPLAY_MODE": {
                return action.payload;
            }
            default: {
                return state;
            }
        }
    }
    > 
    const todoListReducer = function (state = [], action) {
        switch (action.type) {
            case "ADD_TODO": {
                return [...state, {text: action.payload, status: false}];
            }
            case "DEL_TODO": {
                return [...state.slice(0, action.payload), ...state.slice(action.payload + 1)]
            }
            case "TOGGLE": {
                return [...state.slice(0, action.payload),
                    Object.assign({}, state[action.payload], {status: !state[action.payload][state]}),
                    ...state.slice(action.payload + 1)]
            }
            default: {
                return state;
            }
        }
    }
    

    用函数创建action

    刚才可以看到所有action都是手动打的, 比较麻烦, 可以考虑把生成指令组合成一些方法, 自动生成action对象.

    const addTodo = (text) => ({
            type:"ADD_TODO",
            payload:text
        })
    > 
    const delTodo = (index) => ({
        type:"DEL_TODO",
        payload:index
    })
    > 
    const changeDisplayMode = (modeId) => ({
        type:"CHANGE_DISPLAY_MODE",
        payload:modeId
    })
    > 
    const toggle = (index) => ({
        type:"TOGGLE",
        payload:index
    })
    

    小结

    到这一步, Redux的数据处理逻辑部分都已经完全实现了, 总共就用了量个API, createStore用来创建store, combineReducers用来合并所有的reducer. 可以说是相当简练了.

    • 设计数据模型, 确定好state的结构
    • 解剖state, 将每一段state的属性做成reducer, 每个reducer里加入处理逻辑
    • 用combineReducers将所有reducer合并成一个reducer
    • 用createStore将reducer制作成store
    • 设计指令模型, 将指令对象用函数生成
    • 此时就可以用store.subscribe()来监听指令了, 用store.dispatch()来发送指令.

    相关文章

      网友评论

          本文标题:从0开始的Redux旅程(一)

          本文链接:https://www.haomeiwen.com/subject/zqhxfxtx.html