我对Redux的原理理解

293 阅读6分钟
 redux 实现原理是什么?能解决那些问题
随着 JavaScript 单页应用开发日趋势复杂,管理不断变化的state 非常困难
Redux的出现解决了state 数据问题。
这里我用一个最近比较火的电视剧 《安家》来简单做一个demo。来为大家介绍一下
故事开始拉!
首先 有一个客户到安家天下房屋中介找房子 ,那这里有一个先决条件中介和客户。
一、 我们先创建一个场景 建立一个文件夹app 并创建一个index.html

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Redux的实现原理</title>
    </head>
    <body>
        <!-- 找房子的用户 -->
        <div id='user_name'></div>
        <div id='user_data'></div>
        <!-- 安家天下房屋中介 -->
        <div id='intermediaary'></div>
        <div id='inter_data' ></div id='inter_data'>
        <!-- 故事脚本-->
        <script src="app.js"></script>
    </body>
</html>

二、 接下来创建我们故事的脚步 app.js

//1、初始化数据 定义好角色 张三来安家找找房子
let appStore = {
  user: { name: "张三", data: "找房子" },
  intermediaary: { name: "安家天下", data: "房四井接待办理找房子" }
};
//输出用户的需求
function renderUser(user) {
  let element = document.querySelector("#user_name");
  element.innerHTML = user.name; //用户张三需要找房子
  let ev = document.querySelector("#user_data");
  ev.innerHTML = user.data;
}
//中介处理的需求
function renderInermediaay(intermediaary) {
  let element = document.querySelector("#intermediaary");
  element.innerHTML = intermediaary.name;
  let ev = document.querySelector("#inter_data");
  ev.innerHTML = intermediaary.data; //安家天下房四井
}
//定义函数 并且把内容输出
function renderApp(appStore) {
  renderUser(appStore.user);
  renderInermediaay(appStore.intermediaary);
}
renderApp(appStore); //执行脚本
张三找房子 安家天下方四井开始接待了,那这里有什么问题呢?
状态是一个全局变量 ,安全性低 任何地方都可以改 ?增加修改的门槛不能随便改。
这时候我们需要一个action 创建一个dispatch函数
三、 这里我们先定义好房屋中介能做到的常量

//定义好房子中介和用户之间都懂的事
const UPDATE_USER_NAME = "UPDATE_USER_NAME"; //用户的名字
const UPDATE_USER_DATA = "UPDATE_USER_DATA"; //用户要做什么事情
const UPDATE_INTERMEDIAARY_NAME = "UPDATE_INTERMEDIAARY_NAME"; //中介的名字
const UPDATE_INTERMEDIAARY_DATA = "UPDATE_CONTEN_TEXT"; //中介能接待的事情
//action 派发分发动作  描述你想做什么? 是一个普通的属性 必须有一个type
function dispatch(action) {
  switch (action.type) {
    case UPDATE_USER_NAME:
      appStore.user.name = action.name;
      break;
    case UPDATE_USER_DATA:
      appStore.user.data = action.data;
      break;
    case UPDATE_INTERMEDIAARY_NAME:
      appStore.intermediaary.name = action.name;
      break;
    case UPDATE_INTERMEDIAARY_DATA:
      appStore.intermediaary.data = action.data;
      break;
    default:
         throw new Error("臣妾做不到啊!");
  }
}
//李四来安家卖房子了
setTimeout(() => {
  dispatch({ type: UPDATE_USER_NAME, name: "李四" });
  dispatch({ type: UPDATE_USER_DATA, data: "来卖房子了" });
  dispatch({ type: UPDATE_INTERMEDIAARY_DATA, data: "王子接待买房子业务" });
  renderApp(appStore); //执行脚本
}, 1000);
四、 这里能保证访问中介只能做这四个事情 其他一概不处理 增量一点安全想但是没有解决appStore 全局变量的问题。
所以我们创建一个 函数把appStore 放进去 createStore
1、把全局变量放进 createStore 中
2、dispatch也放进去 并创建获取当前state functon

// 添加函数仓库
function createStore() {
  //1、初始化数据 定义好角色 张三来安家找找房子
  let state = {
    user: { name: "张三", data: "找房子" },
    intermediaary: { name: "安家天下", data: "房四井接待办理找房子" }
  };
  // 创建一个获取当前state的方法
  function getState() {
    return JSON.parse(JSON.stringify(state)); //深拷贝一下
  }
  //action 派发分发动作  描述你想做什么? 是一个普通的属性 必须有一个type
  function dispatch(action) {
    switch (action.type) {
      case UPDATE_USER_NAME:
        state.user.name = action.name;
        break;
      case UPDATE_USER_DATA:
        state.user.data = action.data;
        break;
      case UPDATE_INTERMEDIAARY_NAME:
        state.intermediaary.name = action.name;
        break;
      case UPDATE_INTERMEDIAARY_DATA:
        state.intermediaary.data = action.data;
        break;
      default:
        throw new Error("臣妾做不到啊!");
    }
  }
  return { getState, dispatch };
}

这样解决了全局变量不可控问题。

五、 新问题来了每个应用处理逻辑不同 我们不能把 dispatch 直接放在createStore 下 否则 我们需要建立多个 状态管理,这个是没有必要的所以这里需要进一步优化添加一个处理函数也就是我们的reducer.并添加用户的初始化状态.

// 添加函数仓库
function createStore(reducer) {
  //1、初始化数据 定义好角色 张三来安家找找房子
  let state; //第一次动作是空对象没有
  // 创建一个获取当前state的方法
  function getState() {
    return JSON.parse(JSON.stringify(state)); //深拷贝一下
  }
  //action 派发分发动作  描述你想做什么? 是一个普通的属性 必须有一个type
  function dispatch(action) {
    state = reducer(state, action);
  }
  dispatch({}); //获取初始化状态
  return { getState, dispatch };
}
//添加用户初始化状态
let initState = {
  user: { name: "张三", data: "找房子" },
  intermediaary: { name: "安家天下", data: "房四井接待办理找房子" }
};

//添加处理器 :根据老的状态处理返回新的状态
let reducer = function(oldState = initState, action) {
  switch (action.type) {
    case UPDATE_USER_NAME:
      //这里需要注意一定要返回一个新的对象
      return { ...oldState, user: { ...oldState.data, name: action.name } };
    // break;
    case UPDATE_USER_DATA:
      return { ...oldState, user: { ...oldState.user, data: action.data } };
    case UPDATE_INTERMEDIAARY_NAME:
      return {
        ...oldState,
        intermediaary: { ...oldState.intermediaary, name: action.name }
      };
    case UPDATE_INTERMEDIAARY_DATA:
      return {
        ...oldState,
        intermediaary: { ...oldState.intermediaary, data: action.data }
      };
    default:
      // throw new Error("臣妾做不到啊!");
      return oldState; //返回老的状态
  }
};

let store = createStore(reducer);
renderApp(store.getState());
//李四来安家卖房子了
setTimeout(() => {
  store.dispatch({ type: UPDATE_USER_NAME, name: "李四" });
  store.dispatch({ type: UPDATE_USER_DATA, data: "来卖房子了" });
  store.dispatch({
    type: UPDATE_INTERMEDIAARY_DATA,
    data: "王子接待买房子业务"
  });
  // console.log(store.getState());
  renderApp(store.getState());
}, 1000);


解决了在各种环境使用问题 接下来 看看还有哪些问题?
没错 就是 renderApp 目前我们的程序每次都需要自己渲染 手动去调用很不方便,

六 、添加发布订阅 让我们的状态自动刷新 修改createStore

//输出用户的需求
function renderUser(user) {
  let element = document.querySelector("#user_name");
  element.innerHTML = user.name; //用户张三需要找房子
  let ev = document.querySelector("#user_data");
  ev.innerHTML = user.data;
}
//中介处理的需求
function renderInermediaay(intermediaary) {
  let element = document.querySelector("#intermediaary");
  element.innerHTML = intermediaary.name;
  let ev = document.querySelector("#inter_data");
  ev.innerHTML = intermediaary.data; //安家天下房四井
}
//定义好房子中介和用户之间都懂的事
const UPDATE_USER_NAME = "UPDATE_USER_NAME"; //用户的名字
const UPDATE_USER_DATA = "UPDATE_USER_DATA"; //用户要做什么事情
const UPDATE_INTERMEDIAARY_NAME = "UPDATE_INTERMEDIAARY_NAME"; //中介的名字
const UPDATE_INTERMEDIAARY_DATA = "UPDATE_CONTEN_TEXT"; //中介能接待的事情

// 添加函数仓库
function createStore(reducer) {
  let listeners = []; //存储所有状态
  let state; //第一次动作是空对象没有
  // 创建一个获取当前state的方法
  function getState() {
    return JSON.parse(JSON.stringify(state)); //深拷贝一下
  }
  //action 派发分发动作  描述你想做什么? 是一个普通的属性 必须有一个type
  function dispatch(action) {
    state = reducer(state, action);
    listeners.forEach(fn => fn());
  }
  dispatch({}); //获取初始化状态
  //添加订阅模式 ,供外部订阅本仓库中的状态的变化 ,如果状态变化发生了改变就执行该程序
  function subscribe(listener) {
    listeners.push(listener); //添加订阅
      //取消 已发布订阅
    return function() {
      listeners = listeners.filter(item => {
        //过滤数组
        listener != item;
      });
    };
  }
  return { getState, dispatch, subscribe };
}
//添加用户初始化状态
let initState = {
  user: { name: "张三", data: "找房子" },
  intermediaary: { name: "安家天下", data: "房四井接待办理找房子" }
};

//添加处理器 :根据老的状态处理返回新的状态
let reducer = function(oldState = initState, action) {
  switch (action.type) {
    case UPDATE_USER_NAME:
      console.log({
        ...oldState,
        user: { ...oldState.user, data: action.data }
      });
      return { ...oldState, user: { ...oldState.data, name: action.name } };
    case UPDATE_USER_DATA:
      // state.user.data = action.data;
      // break;
      return { ...oldState, user: { ...oldState.user, data: action.data } };
    case UPDATE_INTERMEDIAARY_NAME:
      return {
        ...oldState,
        intermediaary: { ...oldState.intermediaary, name: action.name }
      };
    case UPDATE_INTERMEDIAARY_DATA:
      return {
        ...oldState,
        intermediaary: { ...oldState.intermediaary, data: action.data }
      };
    default:
      // throw new Error("臣妾做不到啊!");
      return oldState; //返回老的状态
  }
};

let store = createStore(reducer);
//定义函数 并且把内容输出
function renderApp() {
  renderUser(store.getState().user);
  renderInermediaay(store.getState().intermediaary);
}
renderApp();
//添加订阅
store.subscribe(renderApp);
//李四来安家卖房子了
setTimeout(() => {
  store.dispatch({ type: UPDATE_USER_NAME, name: "李四" });
  store.dispatch({ type: UPDATE_USER_DATA, data: "来卖房子了" });
  //注意 用户没有更换安家天下中介 😁
  store.dispatch({
    type: UPDATE_INTERMEDIAARY_DATA,
    data: "王子接待买房子业务"
  });
}, 1000);
好了我们的客户可以任意的到我们的房屋中介来办理买卖房屋业务了 是不是很简单 。