博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redux简单实现与分析
阅读量:6267 次
发布时间:2019-06-22

本文共 5672 字,大约阅读时间需要 18 分钟。

随着单页开发日趋复杂,管理不断变化的state非常困难。在React中,数据在组件中是单向流动的。数据从一个方向父组件流向子组件(通过props),但是,两个非父子关系的组件(或者称作兄弟组件)之间的通信就比较麻烦(可以通过父给子传递方法,子通过这个方法修改父的数据,后父传给另一个子组件来实现)。Redux的出现就是为了解决state里的数据问题

1.Redux概念解析

1.1 Store

  • Store就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个Store
  • Redux 提供createStore这个函数,用来生成Store
import { createStore } from 'redux';const store = createStore(fn);//createStore函数接受另一个函数作为参数,返回新生成的Store对象。复制代码

1.2 State

Store对象包含所有数据。如果想得到某个时点的数据,就要对Store生成快照。这种时间点的数据集合,就叫做State。 当前时刻的State,可以通过store.getState()拿到。

import { createStore } from 'redux';const store = createStore(fn);const state = store.getState();复制代码

Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。

1.3 Action

State的变化,会导致View的变化。但是,用户接触不到 State,只能接触到View 。Action 就是 View 发出的通知,表示State 应该怎样变化,它会运送数据到 Store。 Action是一个对象。其中的type属性是必须的,表示 Action 的名称。

const action = {  type: 'ADD_TODO',  payload: '学习redux'};复制代码

1.4 Action Creator

View要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。

const ADD_TODO = '添加 TODO';function addTodo(text) {  return {    type: ADD_TODO,    text  }}const action = addTodo('学习Redux');复制代码

上面代码中,addTodo函数就是一个 Action Creator。

1.5 store.dispatch()

store.dispatch()是 View 发出 Action 的唯一方法。

import { createStore } from 'redux';const store = createStore(fn);store.dispatch({  type: 'ADD_TODO',  payload: '学习Redux'});复制代码

上面代码中,store.dispatch接受一个 Action 对象作为参数,将它发送出去。 结合 Action Creator,这段代码可以改写如下。

store.dispatch(addTodo('学习Redux'))复制代码

1.6 Reducer

Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。 这种 State 的计算过程就叫做 Reducer。 Reducer 是一个纯函数,它接受 当前 State 和Action作为参数,返回一个新的 State。

const reducer = function (state, action) {  return new_state;};复制代码

2.redux 简单实现

知道了上面redux的基本概念,我们来实现一个简单的redux,相信你可以更了解redux。

2.1简单起步

function createStore(reducer) {   }let store=createStore(reducer);复制代码

reducer是我们定的,我们想让state怎样改,reducer中这是,这是我们自己写的。为什么传入的是reducer?因为createStore是创建state的集合store的,而reducer

Reducer 是一个纯函数,它接受 当前 State 和Action作为参数,返回一个新的 State。

所以createStore内部是通过Reducer返回的state。

2.2 将reducer中的state覆盖createStore的state

reducer返回一个新的state。我们怎样将返回的state覆盖store中对应的state呢?state不能直接修改,只能通过dispatch派发action让reducer修改

如果一不小心state='',则state将被清空,为了避免错误的修改。修改state只能通过dispatch。

dispatch不是发送action的吗?

对,dispatch内部是通过reducer使用action返回的新state赋值给旧的state

function createStore(reducer) {    let state;//此时默认还是undefined    function dispatch(action) {//派发        state=reducer(state,action);    }    dispatch({});//目的是第一次时将默认状态覆盖掉自身状态}function reducer(state={title:'标题'},action) {    switch (action.type){           }    return state;}复制代码

2.3 getState

getState是唯一获得state的方法,但是getState获得的事原state的深拷贝对象,防止被篡改。

function createStore(reducer) {    let state;    function dispatch(action) {        state=reducer(state,action);    }    dispatch({});    let getState=()=>JSON.parse(JSON.stringify(state));    return(getState,dispatch)}function reducer(state={title:'标题'},action) {    switch (action.type){           }    return state;}复制代码

2.4完善reducer

function createStore(reducer) {    let state;    function dispatch(action) {        state=reducer(state,action);    }    dispatch({});    let getState=()=>JSON.parse(JSON.stringify(state));    return(getState,dispatch)}//以下需要用户自己写const CHANGE_TITLE='change_title'function reducer(state={title:'标题'},action) {    switch (action.type){        case CHANGE_TITLE://{
type:CHANGE_TITLE,content:'xx'} return {...state,title:action.content} } return state;}let store=createStore(reducer);function render() { document.querySelector('.title').innerHTML=store.getState().title}render();复制代码

2.5调用dispatch

setTimeout(function () {    store.dispatch({
type:HANGE_TITLE,content:'住'}); render();},2000)setTimeout(function () { store.dispatch({
type:HANGE_TITLE,content:'住'}); render();},2000)复制代码

现在我们调用了两次setTimeout,需要两次render方法。如果有多个,需要多次调用render。现在我们通过发布订阅将render,或其他的方法订阅。每次dispatch后都调用这些方法。

2.6发布订阅 subscribe

function createStore(reducer) {    let state;    function dispatch(action) {        state=reducer(state,action);        listeners.forEach(item=>item());    }    let listeners=[]//存放所有的监听函数    let subscribe=(fn)=>{        listeners.push(fn);    }    dispatch({});    let getState=()=>JSON.parse(JSON.stringify(state));    return(getState,dispatch,subscribe)}let store=createStore(reducer);store.subscribe(render);//订阅render方法//也可以是其他方法store.subscribe(function(){    alert(1111));function render() {    document.querySelector('.title').innerHTML=store.getState().title}render();setTimeout(function () {    store.dispatch({
type:HANGE_TITLE,content:'住'});},2000)setTimeout(function () { store.dispatch({
type:HANGE_TITLE,content:'住'});},2000)复制代码

2.7 发布订阅 unSubscribe

上面方法中有一次我们订阅了alert(1111)的方法,如果我们想让第一次setTimeout运行这个方法,第二次就不需要了。这时候就需要unSubscribe

执行subscrib时,返回一个unSubscribe的方法

let unSubscribe=store.subscribe(function(){    alert(1111));复制代码
function createStore(reducer) {    let state;    function dispatch(action) {        state=reducer(state,action);        listeners.forEach(item=>item());    }    let listeners=[]//存放所有的监听函数    let subscribe=(fn)=>{        listeners.push(fn);        return ()=>{//取消绑定的函数,调用可以删除函数            listeners=listeners.filter(item=>item!==fn);        }    }    dispatch({});    let getState=()=>JSON.parse(JSON.stringify(state));    return(getState,dispatch,subscribe)}store.subscribe(render);let unSubscribe=store.subscribe(function(){    alert(1111));function render() {    document.querySelector('.title').innerHTML=store.getState().title}render();setTimeout(function () {    store.dispatch({
type:HANGE_TITLE,content:'住'});+ unSubscribe()},2000)setTimeout(function () { store.dispatch({
type:HANGE_TITLE,content:'住'});},2000)复制代码

转载地址:http://egdpa.baihongyu.com/

你可能感兴趣的文章
SAP
查看>>
读掘金小册组件精讲总结2
查看>>
MVC项目中怎样用JS导出EasyUI DataGrid为Excel
查看>>
制作个人开发IDE
查看>>
给架构师骂了
查看>>
ajax提交form表单资料详细汇总
查看>>
Excel——使用INDEX和SMALL实现条件筛选
查看>>
c#迭代器 转载
查看>>
JQuery与JavaScript
查看>>
Jmeter--正则表达式提取器
查看>>
设置Slider Control 控件的取值范围
查看>>
struts2 启动tomcat时报错:org.apache.catalina.core.StandardContext filterStart
查看>>
asp.net导入后台代码
查看>>
java web dev知识积累
查看>>
Flex 经纬度匹配正则表达式
查看>>
在SSIS包中使用 Checkpoint从失败处重新启动包[转]
查看>>
为什么开通博客?
查看>>
深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)
查看>>
LVS+Keepalived实现高可用负载均衡(转)
查看>>
Django学习【第14篇】:Django之Form组件补充
查看>>