Redux

介绍

什么是redux

Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动

redux_data_flow.png

 

动机

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

为什么要管理状态

管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。

跟随 Flux、CQRS 和 Event Sourcing 的脚步,通过限制更新发生的时间和方式,Redux 试图让 state 的变化变得可预测。这些限制条件反映在 Redux 的三大原则中。

生态系统

Redux 是一个体小精悍的库,但它相关的内容和 API 都是精挑细选的,足以衍生出丰富的工具集和可扩展的生态系统。

react-redux

帮助用户管理store中的状态

安装

cnpm install react-redux --save

仓库的引用

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "store"
import App from "view";
ReactDOM.render(
    <Provider store={store}>
   <App/>
    </Provider>,
    document.getElementById("#app")
)

页面注入数据

写法一:

import React from "react";
import {connect} from "react-redex";
class App extends React.Component{
    render(){
   const { datalist } = [];
   return <div>
    <Children datalist={datalist}></Children>
   </div>
    }
}
const mapStateToProps = (state)=>{
    return state.reducer;
}
const mapDispatchToProps = (dispatch)=>{
    return {
   update(){
    dispatch({type:"",payload})
   }
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(App);

写法二:

通过 es7 Decorator 装饰器完成

import React from "react";
import {connect} from "react-redex";
const mapStateToProps = (state)=>{
    return state.reducer;
}
const mapDispatchToProps = (dispatch)=>{
    return {
   update(){
    dispatch({type:"",payload})
   }
    }
}
 @connext(mapStateToProps,mapDispatchToProps)
class App extends React.Component{
    render(){
   const { datalist } = [];
   return <div>
    <Children datalist={datalist}></Children>
   </div>
    }
}
 
export default App;

装饰器

decorator(装饰器)是ES7里面的一个语法糖,作用于类、类属性\方法,为它们提供一个实现与业务逻辑无关的功能的接口。

安装

//babel 6.0

npm install babel-plugin-transform-decorator --save

//babel 7.0

npm install @babel/plugin-proposal-decorators

配置

.babelrc
 {
   "plugins": ["@babel/plugin-proposal-decorators"]
 }

wepy-redux

安装

cnpm install redux redux-actions redux-promise wepy-redux --save

目录结构

store_close.png

store_open.png

创建 store

import { createStore, applyMiddleware } from "redux";
import Reducers from "./reducer";
import promiseMiddleware from "redux-promise";
//写法一:
export default function configStore() {
    return createStore(Reducers, applyMiddleware(promiseMiddleware))
}
//写法二
export default createStore(Reducers, applyMiddleware(promiseMiddleware))

创建 reducer

rank.js

const defaultState = {
   rankList: [1, 2, 3]
}
const rankReducer = (state = defaultState, action) => {
   const { type, payload } = action;
   switch (type) {
     case "UPDATE":
       return { ...state, rankList: payload }
     default:
       return state;
   }
}
export default rankReducer;

index.js

import { combineReducers } from "redux";
import rankReducer from "./rank";
import topListReducer from "./toplist";
import searchReducer from "./search";
const Reducers = combineReducers({
   rankReducer,
   topListReducer,
   searchReducer,
   ...
})
export default Reducers;

创建 action

import { RNAK_UPDATE } from "../type/rank";
import { createAction } from "redux-actions";
import axios from "@/utils/request";
//方式一:
export function update(payload) {
   return {
     type: RNAK_UPDATE,
     payload
   }
}
//方式二:
const getJson = async function(url) {
   let result = await axios.get(url);
   return result.data.data.slider;
}
export const update = createAction(RNAK_UPDATE, () => {
   const url = "https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg";
   return getJson(url);
})

创建 type

export const RNAK_UPDATE = "UPDATE";
export const TOPLIST_UPDATE = "UPDATE";

绑定及监听

在app.wepy文件中,添加下面代码:

import { setStore } from 'wepy-redux'

import store from './store'

setStore(store)

setStore()是用来将仓库中的数据绑定到页面中

类似react-redux中的 <Provider store={store}></Provider>组件

Store

职责:

  • action 描述“发生了什么”的一个动作,通过dispatch 来执行这个动作。
  • reducers 来根据 action 更新 state 的用法。
  • store 就是把它们联系到一起的对象

基础

  • 提供 createStore() 方法创建一个仓库;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 unsubscribe(listener) 返回的函数注销监听器。

再次强调一下 Redux 应用只有一个单一的 store。

 

配置初始化数据

createStore() 的第二个参数是可选的, 用于设置 state初始状态。这对开发应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。

let store = createStore(reducer, window.STATE_FROM_SERVER)

添加监听

当仓库状态改变时,页面数据也需要随着改变,通过subscribe监听数据变化,并绑定到视图

import store from "store";
    import React from "react";
    import ReactDOM from "react-dom";
    
    const template = <div></div>;
    const element = document.getElementById("#app");
    const render = ()=>{
        ReactDOM.render(template,element)
    }
    render()
    //只能监听一个函数,所以对render方法改造
    store.subcribe(render)

移除监听

创建监听后,store.subscribe() 将返回一个函数作为返回值,调用这个函数,即可移除对视图监听

const unsubscribe  = store.subscribe(listener);
     unsubscribe()

Action

Action 是把数据从应用传到 store 的有效载荷。 它是 store 数据的唯一来源。 一般来说你会通过 store.dispatch() 将 action 传到 store。

const ADD_TODO = 'ADD_TODO'
    {
      type: ADD_TODO,
      text: 'Build my first Redux app'
    }
import { PLUS, MINUS } from './store/action'

除了 type 字段外,action 对象的结构完全由你自己决定。参照 Flux 标准 Action 获取关于如何构造 action 的建议。

这时,我们还需要再添加一个 action index 来表示用户完成任务的动作序列号。因为数据是存放在数组中的,所以我们通过下标 index 来引用特定的任务。而实际项目中一般会在新建数据的时候生成唯一的 ID 作为数据的引用标识。

{
      type: "PLUS",
      index: 1
    }
{
      type: SET_VISIBILITY_FILTER,
      filter: SHOW_COMPLETED
    }

发起Action

通过 dispatch()来发起一个Action, 接受一个对象,对象中有 type,payload

store.dispatch( { type:"PLUS",payload:[something]})

Reducer

指定了应用状态的变化如何响应 actions 并发送到 store 的 reducer 接收旧的 state 和 action,返回新的 state。

import {createStore} from "redux";
    const defaultState = {
        count:0
    }
    const reducer = (state = defaultState,action)=>{
        const { type,payload } = action;
        switch (type) {
             case "PLUS":
                //写法一:
                 return {...state, count: state.count+1}
             case "MINUS":
                //写法二:
                 return Object.assign({},state,{
                    count: state.count-1
                 })
             default:
                return state;
         }
    }
    const store = createStore(reducer)

注意事项

永远不要在 reducer 里做这些操作:

  • 修改传入参数
  • 执行有副作用的操作,如 API 请求和路由跳转
  • 调用非纯函数,如 Date.now() 或 Math.random()

纯函数

为什么要使用纯函数?

当我们的程序变得庞大的时候, 将不可避免地引发一些bugs。我们不能保证杜绝bug产生, 但是我们可以通过某些编程方式来减少一些错误的发生。

纯函数就是其中一种,它也是函数式编程中一部分。那它为什么可以起到减少bug的作用呢, 原因就在于能被称之为纯函数而制定的一些原则,我们来简单看下

3个原则:

  • 变量都只在函数作用域内获取, 作为的函数的参数传入
  • 不会产生副作用(side effects), 不会改变被传入的数据或者其他数据
  • 相同的输入 一定 保证相同的输出(same input -> same ouput)

纯函数的一些优点

  • 容易测试(testable)
  • 因为相同的输入必定是相同的输出,因此结果可以缓存(cacheable)
  • 自我记录(Self documenting),因为需要的变量都是参数,参数命名良好的情况下即便很久以后再去看这个函数依旧可以很容易知道这个函数需要哪些参数
  • 因为不用担心有副作用(side-effects),因此可以更好地工作

合并 reducer

需求

当我们的业务逻辑足够复杂的时候,一个全局的state 已经不能很好维持每个页面中的共享数据,迫切的需要进行作用域隔离 combineReducers 函数应用而生

import { combineReducers } from 'redux';
    const reducer1 = ()=>{};
    const reducer2 = ()=>{};
    const Reducers  = combineReducers({
        reducer1:reducer1,
        reducer2,
        ...
    })
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值