redux 實現原理是什麼?能解決那些問題
隨着 JavaScript 單頁應用開發日趨勢複雜,管理不斷變化的state 很是困難
Redux的出現解決了state 數據問題。
這裏我用一個最近比較火的電視劇 《安家》來簡單作一個demo。來爲你們介紹一下
故事開始拉!
首先 有一個客戶到安家天下房屋中介找房子 ,那這裏有一個先決條件中介和客戶。
1、 咱們先建立一個場景 創建一個文件夾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>複製代碼
2、 接下來建立咱們故事的腳步 app.js
//一、初始化數據 定義好角色 張三來安家找找房子
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函數
3、 這裏咱們先定義好房屋中介能作到的常量
//定義好房子中介和用戶之間都懂的事
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);複製代碼
4、 這裏能保證訪問中介只能作這四個事情 其餘一律不處理 增量一點安全想可是沒有解決appStore 全局變量的問題。
因此咱們建立一個 函數把appStore 放進去 createStore
一、把全局變量放進 createStore 中
二、dispatch也放進去 並建立獲取當前state functon
// 添加函數倉庫
function createStore() {
//一、初始化數據 定義好角色 張三來安家找找房子
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 };
}複製代碼
這樣解決了全局變量不可控問題。
5、 新問題來了每一個應用處理邏輯不一樣 咱們不能把 dispatch 直接放在createStore 下 不然 咱們須要創建多個 狀態管理,這個是沒有必要的因此這裏須要進一步優化添加一個處理函數也就是咱們的reducer.並添加用戶的初始化狀態.
// 添加函數倉庫
function createStore(reducer) {
//一、初始化數據 定義好角色 張三來安家找找房子
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);複製代碼
好了咱們的客戶能夠任意的到咱們的房屋中介來辦理買賣房屋業務了 是否是很簡單 。