day-080-eighty-20230529-復(fù)合組件通信redux-redux源碼-redux工程化開(kāi)發(fā)-自定義hook
復(fù)合組件通信redux
- 要想實(shí)現(xiàn)復(fù)合組件通信,一般采用公共狀態(tài)管理方案。
- 常見(jiàn)的公共狀態(tài)管理方案:
- 官方推薦的:redux。
-
redux
+react-redux
+redux-logger
/redux-promise
/redux-saga
/redux-thunk
:中間件。- 代表:dva「redux-saga 」或 umi。
-
@reduxjs/toolkit
:工具類。
-
-
mobx
。 -
zustand
。 - …
- 官方推薦的:redux。
redux的應(yīng)用場(chǎng)景
-
redux
在以下情況下更有用:- 在應(yīng)用的大量地方,都存在大量的狀態(tài)。
- 應(yīng)用狀態(tài)會(huì)隨著時(shí)間的推移而頻繁更新。
- 更新該狀態(tài)的邏輯可能很復(fù)雜。
- 中型和大型代碼量的應(yīng)用,很多人協(xié)同開(kāi)發(fā)。
redux庫(kù)和相關(guān)工具
-
redux
是一個(gè)小型的獨(dú)立js庫(kù)
, 但是它通常與其他幾個(gè)包一起使用:-
react-redux
:react-redux是我們的官方庫(kù),它讓React組件
與redux
有了交互,可以從store
讀取一些state
,可以通過(guò)dispatch
actions
來(lái)更新store
! -
redux Toolkit
:redux Toolkit
是我們推薦的編寫redux邏輯的方法
。 它包含我們認(rèn)為對(duì)于構(gòu)建redux應(yīng)用程序
必不可少的包和函數(shù)。redux Toolkit
構(gòu)建在我們建議的最佳實(shí)踐中,簡(jiǎn)化了大多數(shù)redux任務(wù)
,防止了常見(jiàn)錯(cuò)誤,并使編寫redux應(yīng)用程序
變得更加容易。 -
redux DevTools 拓展
:Redux DevTools Extension
可以顯示redux存儲(chǔ)
中狀態(tài)
隨時(shí)間變化的歷史記錄,這允許您有效地調(diào)試應(yīng)用程序。
-
redux基礎(chǔ)工作流程
-
創(chuàng)建公共的容器;
const store = createStore([reducer])
- 內(nèi)部包含兩個(gè)狀態(tài):公共狀態(tài)和事件池。
- 公共狀態(tài):存放
各組件需要通信的信息
。 - 事件池:存放許多方法–一般是
讓組件更新的方法
。
- 公共狀態(tài):存放
- 在內(nèi)部,只要
公共狀態(tài)
被更改,就會(huì)通知事件池中的方法
執(zhí)行!- 目的是:當(dāng)
公共狀態(tài)被修改
,讓事件池中的方法執(zhí)行
,實(shí)現(xiàn)讓相關(guān)組件更新
,這樣組件中就可以實(shí)時(shí)獲取最新的公共信息
了!
- 目的是:當(dāng)
-
reducer
就是公共狀態(tài)管理的管理員
。- 對(duì)于
公共狀態(tài)
的修改
,就要通過(guò)reucer來(lái)執(zhí)行
。
- 對(duì)于
- 內(nèi)部包含兩個(gè)狀態(tài):公共狀態(tài)和事件池。
-
從
公共容器
中獲取公共狀態(tài)
,然后在組件中進(jìn)行渲染
。store.getState()
-
如果
組件中使用了公共狀態(tài)信息
,則我們需要把讓組件更新的函數(shù)
加入到公共容器的事件池
中!store.subscribe(組件更新函數(shù))
- 這是發(fā)布訂閱模式。
-
想要
修改公共狀態(tài)
,需要先通知createStore([reducer])
中的reducer
執(zhí)行,在reducer中修改公共狀態(tài)
!store.dispatch({type:'xxx'})
-
reducer
是公共狀態(tài)管理的管理員
。let initial={公共狀態(tài)} const reducer = function(state=initail,action){ //state: 公共狀態(tài)信息。 //action: 傳遞的對(duì)象。 return state }
-
實(shí)際流程
-
創(chuàng)建文件
/src/store/index.js
,在內(nèi)部創(chuàng)建公共容器并導(dǎo)出。- 代碼:
-
/src/store/index.js
:// 引入createStore用于創(chuàng)建公共容器。 import { createStore } from "redux"; // 定義初始公共狀態(tài),在reducer初始化時(shí)使用。 let initial = { supNum: 10, oppNum: 5, }; // 創(chuàng)建reducer管理員,用于修改容器中的公共狀態(tài)。 const reducer = function reducer(state = initial, action) { // state:容器中管理的公共狀態(tài)信息。實(shí)現(xiàn)的方式:在我們創(chuàng)建好store即createStore(reducer)后,redux內(nèi)部會(huì)默認(rèn)派發(fā)一次,也就是把reducer執(zhí)行一次。而第一次執(zhí)行reducer函數(shù)的時(shí)候,store容器中還沒(méi)有公共狀態(tài)信息,也就是此時(shí)的state=undefined。而我們寫的state=initial,就是為了在此時(shí)給容器賦值初始的狀態(tài)信息。 // action:派發(fā)的行為對(duì)象。store.dispatch(行為對(duì)象)中傳遞的這個(gè)行為對(duì)象就是賦值給action形參的。而store.dispatch()一定會(huì)通知reducer函數(shù)執(zhí)行。action必須是一個(gè)對(duì)象。action對(duì)象中必須具備type屬性,type屬性就是派發(fā)的行為標(biāo)識(shí)。 //我們接下來(lái)就是可以在reducer函數(shù)中,基于action.type的不同,修改不同的狀態(tài)值。 //函數(shù)最后返回的信息,會(huì)整體替換store容器中的公共狀態(tài)信息。 let { type } = action; switch (type) { case "sup": state.supNum++; break; case "opp": state.oppNum++; break; default: } return state; }; // 創(chuàng)建一個(gè)store容器來(lái)管理公共狀態(tài)。 const store = createStore(reducer); export default store;
-
- 引入createStore用于創(chuàng)建公共容器。
- 定義初始公共狀態(tài),在reducer初始化時(shí)使用。
- 創(chuàng)建reducer管理員函數(shù),用于修改容器中的公共狀態(tài)。
- reducer內(nèi)部會(huì)有兩個(gè)形參:
- state:容器中管理的公共狀態(tài)信息。實(shí)現(xiàn)的方式:在我們創(chuàng)建好store即createStore(reducer)后,redux內(nèi)部會(huì)默認(rèn)派發(fā)一次,也就是把reducer執(zhí)行一次。而第一次執(zhí)行reducer函數(shù)的時(shí)候,store容器中還沒(méi)有公共狀態(tài)信息,也就是此時(shí)的state=undefined。而我們寫的state=initial,就是為了在此時(shí)給容器賦值初始的狀態(tài)信息。
- action:派發(fā)的行為對(duì)象。store.dispatch(行為對(duì)象)中傳遞的這個(gè)行為對(duì)象就是賦值給action形參的。而store.dispatch()一定會(huì)通知reducer函數(shù)執(zhí)行。action必須是一個(gè)對(duì)象。action對(duì)象中必須具備type屬性,type屬性就是派發(fā)的行為標(biāo)識(shí)。
- 我們接下來(lái)就是可以在reducer函數(shù)中,基于action.type的不同,修改不同的狀態(tài)值。
- 函數(shù)最后返回的信息,會(huì)整體替換store容器中的公共狀態(tài)信息。
- reducer內(nèi)部會(huì)有兩個(gè)形參:
- 使用createStore函數(shù)創(chuàng)建一個(gè)store公共容器來(lái)管理公共狀態(tài)。
- 導(dǎo)出store公共容器對(duì)象,以便外部進(jìn)行引用。
- 代碼:
-
/src/store/index.js
:import { createStore } from "redux"; /* 創(chuàng)建REDUCER管理員,修改容器中的公共狀態(tài) + state:容器中管理的公共狀態(tài)信息 在我們創(chuàng)建好store后,redux內(nèi)部會(huì)默認(rèn)派發(fā)一次(也就是把reducer執(zhí)行一次);而第一次執(zhí)行reducer函數(shù)的時(shí)候,store容器中還沒(méi)有公共狀態(tài)信息呢,也就是此時(shí)的state=undefined;而我們寫的 state = initial,就是為了在此時(shí)給容器賦值初始的狀態(tài)信息! + action:派發(fā)的行為對(duì)象 store.dispatch({ //傳遞的這個(gè)對(duì)象就是action type:'xxx', ... }) 一定會(huì)通知reducer函數(shù)執(zhí)行,dispatch中傳遞的對(duì)象,就是賦值給action形參的 + action必須是一個(gè)對(duì)象 + action對(duì)象中必須具備 type 屬性「派發(fā)的行為標(biāo)識(shí)」 我們接下來(lái)就可以在reducer函數(shù)中,基于 action.type 的不同,修改不同的狀態(tài)值! + 函數(shù)最后返回的信息,會(huì)整體替換store容器中的公共狀態(tài)信息 */ let initial = { supNum: 10, oppNum: 5, }; const reducer = function reducer(state = initial, action) { let { type } = action; switch (type) { case "sup": state.supNum++; break; case "opp": state.oppNum++; break; default: } return state; }; /* 創(chuàng)建STORE容器,管理公共狀態(tài) */ const store = createStore(reducer); export default store;
-
第一步store創(chuàng)建完畢后,我們會(huì)把 store 掛載到祖先組件的上下文中,這樣以后不論哪個(gè)組件需要用到store,直接從上下文中獲取即可;
-
- 代碼:
-
創(chuàng)建文件
/src/Theme.js
,用于創(chuàng)建上下文對(duì)象并在根組件/src/index.jsx
中引入。將公共容器放在根組件的上下文對(duì)象中。-
代碼:
-
/src/Theme.js
:import { createContext } from "react"; const Theme = createContext(); export default Theme;
-
/src/index.jsx
:// store處理 import Theme from "./Theme"; import store from "./store"; // 把React用Theme.Provider包起來(lái)。 <Theme.Provider value={{ store }}> //.... </Theme.Provider>
-
-
第一步store創(chuàng)建完畢后,我們會(huì)把store掛載到祖先組件的上下文中,這樣以后不論那個(gè)組件需要用到store,直接從上下文中獲取即可。
-
-
在需要用到公共狀態(tài)管理的文件中,通過(guò)上下文對(duì)象獲取到store公共容器。
- 代碼:
-
/src/views/Vote.jsx
:import { useContext } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: const { store } = useContext(Theme);
-
/src/views/VoteMain.jsx
:import { useContext } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: const { store } = useContext(Theme);
-
/src/views/VoteFooter.jsx
:import { useContext } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: const { store } = useContext(Theme);
-
- 代碼:
-
如果一個(gè)組件需要用到store公共容器中的狀態(tài):
- 總體步驟思路:
- 獲取公共容器中狀態(tài)。
- 把更新視圖的方法放到公共容器中。
- 類組件中可以使用this.setState({})或this.forceUpdate()
- 函數(shù)組件中可以使用自定義hook來(lái)減少代碼。
- 代碼:
-
/src/views/Vote.jsx
:import { useContext, useState, useEffect } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: // 獲取公共狀態(tài)信息 // let state = store.getState() // console.log(state); let { supNum, oppNum } = store.getState(); //把讓組件更新的辦法放在事件池中。返回一個(gè)函數(shù)unsubscribe,此函數(shù)執(zhí)行,可以把剛才放在事件池中的函數(shù),從事件池中移除掉。 let [, setRandom] = useState(+new Date()); // setRandom(+new Date()); useEffect(() => { let unsubscribe = store.subscribe(() => { // 讓視圖更新即可。 setRandom(+new Date()); }); return ()=>{ unsubscribe() } }, []);
import React, { useContext, useState, useEffect } from "react"; import Theme from "@/Theme"; import VoteStyle from "./VoteStyle"; import VoteMain from "./VoteMain"; import VoteFooter from "./VoteFooter"; import useForceUpdate from "@/useForceUpdate"; const Vote = function Vote() { const { store } = useContext(Theme); // 獲取公共狀態(tài)信息 // let state = store.getState() // console.log(state); let { supNum, oppNum } = store.getState(); //把讓組件更新的辦法放在事件池中。返回一個(gè)函數(shù)unsubscribe,此函數(shù)執(zhí)行,可以把剛才放在事件池中的函數(shù),從事件池中移除掉。 let [, setRandom] = useState(+new Date()); // setRandom(+new Date()); useEffect(() => { let unsubscribe = store.subscribe(() => { // 讓視圖更新即可。 setRandom(+new Date()); }); return () => { unsubscribe(); }; }, []); return ( <VoteStyle> <h2 className="title"> React其實(shí)也不難! <span>{supNum + oppNum}</span> </h2> <VoteMain /> <VoteFooter /> </VoteStyle> ); }; export default Vote;
-
/src/views/Vote.jsx
- hook版:import React, { useContext } from "react"; import Theme from "@/Theme"; import VoteStyle from "./VoteStyle"; import VoteMain from "./VoteMain"; import VoteFooter from "./VoteFooter"; import useForceUpdate from "@/useForceUpdate"; const Vote = function Vote() { const { store } = useContext(Theme); let { supNum, oppNum } = store.getState(); let remove = useForceUpdate(store); return ( <VoteStyle> <h2 className="title"> React其實(shí)也不難! <span>{supNum + oppNum}</span> </h2> <VoteMain /> <VoteFooter /> </VoteStyle> ); }; export default Vote;
-
/src/views/VoteMain.jsx
:import { useContext, useState, useEffect } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: let { supNum, oppNum } = store.getState(); let [, setRandom] = useState(+new Date()); useEffect(() => { let unsubscribe = store.subscribe(() => { setRandom(+new Date()); }); return ()=>{ unsubscribe() } }, []);
import React, { useContext, useState, useEffect } from "react"; import Theme from "@/Theme"; const VoteMain = function VoteMain() { const { store } = useContext(Theme); let { supNum, oppNum } = store.getState(); let [, setRandom] = useState(+new Date()); useEffect(() => { store.subscribe(() => { setRandom(+new Date()); }); return; }, []); return ( <div className="main-box"> <p>支持人數(shù):{supNum} 人</p> <p>反對(duì)人數(shù):{oppNum} 人</p> </div> ); }; export default VoteMain;
-
/src/views/VoteMain.jsx
- hook版:import React, { useContext } from "react"; import Theme from "@/Theme"; import useForceUpdate from "@/useForceUpdate"; const VoteMain = function VoteMain() { const { store } = useContext(Theme); let { supNum, oppNum } = store.getState(); useForceUpdate(store); return ( <div className="main-box"> <p>支持人數(shù):{supNum} 人</p> <p>反對(duì)人數(shù):{oppNum} 人</p> </div> ); }; export default VoteMain;
-
/src/useForceUpdate.js
- hook函數(shù):文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-464456.htmlimport { useState, useEffect } from "react"; export default function useForceUpdate(store) { let [, setRandom] = useState(+new Date()); // 但這個(gè)useEffect還得等后期組件渲染完成后才執(zhí)行內(nèi)部的函數(shù)。 let unsubscribe; useEffect(() => { // 組件第一次渲染完畢,把讓組件更新的辦法放在事件池中。 // 執(zhí)行subscribe會(huì)返回unsubscribe,目的是用于移除剛才加入事件池中的方法。 unsubscribe = store.subscribe(() => { setRandom(+new Date()); }); return () => { // 組件銷毀的時(shí)候,把放在事件池中的方法移除掉。 unsubscribe(); unsubscribe = null; }; }, []); // 手動(dòng)返回一個(gè)remove函數(shù),remove可以通過(guò)閉包,訪問(wèn)到當(dāng)前作用域中的unsubscribe,當(dāng)組件渲染之后,unsubscribe就是useEffect中賦值的值。 // 手動(dòng)返回一個(gè)remove函數(shù): 等待后期。 return function remove() { if (unsubscribe) { unsubscribe(); } }; }
-
- 總體步驟思路:
-
如果一個(gè)組件需要修改store公共容器中的狀態(tài):
- 代碼:
-
/src/views/VoteFooter.jsx
:import { useContext } from "react"; import Theme from "@/Theme";//`@/`表示`/src/`; //在函數(shù)組件內(nèi)部: const { store } = useContext(Theme); store.dispatch({ type: "opp" })
import React, { useContext } from "react"; import Theme from "@/Theme"; import { Button } from "antd"; const VoteFooter = function VoteFooter() { const { store } = useContext(Theme); return ( <div className="footer-box"> <Button type="primary" onClick={() => { store.dispatch({ type: "sup" }); }} > 支持 </Button> <Button type="primary" danger onClick={() => { store.dispatch({ type: "opp" }); }} > 反對(duì) </Button> </div> ); }; export default VoteFooter;
-
- 代碼:
redux源碼
-
源碼內(nèi)有一個(gè)函數(shù)createStore()是用來(lái)創(chuàng)建store容器的。
- 入?yún)educer:是一個(gè)管理員函數(shù),里面可以傳一個(gè)狀態(tài)及處理狀態(tài)修改的函數(shù)。
- 返回值:
{getState,subscribe,dispatch,}
。- 返回值.getState():獲取公共狀態(tài)。
- 返回值.subscribe():用于讓
函數(shù)
進(jìn)入事件池
中,即讓函數(shù)
訂閱公共狀態(tài)改變的事件
。 - 返回值.dispatch():派發(fā)事件,讓公共狀態(tài)可以被變動(dòng)。
-
createStore()
內(nèi)部有一個(gè)初始為undefined的公共狀態(tài)對(duì)象和一個(gè)初始為空數(shù)組的事件池。
-
源碼示例:
yarn add lodash//安裝lodash。
//這個(gè)是下方要用的公共方法 (function (global, factory) { "use strict" if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(global, true) return } factory(global) })( typeof window !== "undefined" ? window : this, function factory(window, noGlobal) { /* 檢測(cè)數(shù)據(jù)類型 */ const toString = Object.prototype.toString, isArray = Array.isArray, typeReg = /^(object|function)$/, fnToString = Function.prototype.toString // 萬(wàn)能檢測(cè)數(shù)據(jù)類型的方法 const isType = function isType(obj) { if (obj == null) return obj + '' let type = typeof obj, reg = /^\[object (\w+)\]$/ return !typeReg.test(type) ? type : reg.exec(toString.call(obj))[1].toLowerCase() } // 檢測(cè)是否為對(duì)象 const isObject = function isObject(obj) { return obj !== null && typeReg.test(typeof obj) } // 檢測(cè)是否是window對(duì)象 const isWindow = function isWindow(obj) { return obj != null && obj === obj.window } // 檢測(cè)是否為函數(shù) const isFunction = function isFunction(obj) { return typeof obj === "function" } // 檢測(cè)是否為數(shù)組或者偽數(shù)組 const isArrayLike = function isArrayLike(obj) { if (isArray(obj)) return true let length = !!obj && 'length' in obj && obj.length if (isFunction(obj) || isWindow(obj)) return false return length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj } // 檢測(cè)是否為一個(gè)純粹的對(duì)象(標(biāo)準(zhǔn)普通對(duì)象) const isPlainObject = function isPlainObject(obj) { if (isType(obj) !== "object") return false let proto, Ctor proto = Object.getPrototypeOf(obj) if (!proto) return true Ctor = proto.hasOwnProperty('constructor') && proto.constructor return isFunction(Ctor) && fnToString.call(Ctor) === fnToString.call(Object) } // 檢測(cè)是否為空對(duì)象 const isEmptyObject = function isEmptyObject(obj) { if (!isObject(obj)) throw new TypeError(`obj is not an object`) let keys = Object.getOwnPropertyNames(obj) if (typeof Symbol !== 'undefined') keys = keys.concat(Object.getOwnPropertySymbols(obj)) return keys.length === 0 } // 檢測(cè)是否為有效數(shù)字 const isNumeric = function isNumeric(obj) { let type = isType(obj) return (type === "number" || type === "string") && !isNaN(+obj) } /* 其它基礎(chǔ)方法 */ // 迭代數(shù)組/偽數(shù)組/對(duì)象「支持中途結(jié)束循環(huán)」 const each = function each(obj, callback) { if (typeof callback !== "function") callback = () => { } if (typeof obj === "number" && !isNaN(obj) && obj > 0) obj = new Array(obj) if (typeof obj === "string") obj = Object(obj) if (!isObject(obj)) return obj if (isArrayLike(obj)) { for (let i = 0; i < obj.length; i++) { let item = obj[i] let res = callback.call(obj, item, i) if (res === false) break } return obj } let keys = Object.getOwnPropertyNames(obj) if (typeof Symbol !== 'undefined') keys = keys.concat(Object.getOwnPropertySymbols(obj)) for (let i = 0; i < keys.length; i++) { let key = keys[i], value = obj[key] let res = callback.call(obj, value, key) if (res === false) break } return obj } // 具備有效期的LocalStorage存儲(chǔ) const storage = { set(key, value) { localStorage.setItem( key, JSON.stringify({ time: +new Date(), value }) ) }, get(key, cycle = 2592000000) { cycle = +cycle if (isNaN(cycle)) cycle = 2592000000 let data = localStorage.getItem(key) if (!data) return null let { time, value } = JSON.parse(data) if ((+new Date() - time) > cycle) { storage.remove(key) return null } return value }, remove(key) { localStorage.removeItem(key) } } // 萬(wàn)能的日期格式化工具 const formatTime = function formatTime(time, template) { try { if (time == null) time = new Date().toLocaleString('zh-CN', { hour12: false }) if (typeof template !== "string") template = "{0}/{1}/{2} {3}:{4}:{5}" let arr = [] if (/^\d{8}$/.test(time)) { let [, $1, $2, $3] = /^(\d{4})(\d{2})(\d{2})$/.exec(time) arr.push($1, $2, $3) } else { arr = time.match(/\d+/g) } return template.replace(/\{(\d+)\}/g, (_, $1) => { let item = arr[$1] || "00" if (item.length < 2) item = "0" + item return item }) } catch (_) { return '' } } // 為對(duì)象設(shè)置不可枚舉的屬性 const define = function define(obj, key, value) { Object.defineProperty(obj, key, { writable: true, configurable: true, enumerable: false, value }) } // 延遲處理函數(shù) const delay = function delay(interval = 1000) { return new Promise(resolve => { let timer = setTimeout(() => { resolve() clearTimeout(timer) }, interval) }) } /* 發(fā)布訂閱設(shè)計(jì)模式 */ let listeners = {} // 向事件池中加入自定義事件及方法 const on = function on(name, callback) { if (typeof name !== 'string') throw new TypeError('name is not a string') if (typeof callback !== 'function') throw new TypeError('callback is not a function') if (!listeners.hasOwnProperty(name)) listeners[name] = [] let arr = listeners[name] if (arr.includes(callback)) return arr.push(callback) } // 從事件池中移除自定義事件及方法 const off = function off(name, callback) { if (typeof name !== 'string') throw new TypeError('name is not a string') if (typeof callback !== 'function') throw new TypeError('callback is not a function') let arr = listeners[name], index if (!Array.isArray(arr)) return index = arr.indexOf(callback) if (index >= 0) arr[index] = null } // 通知指定的自定義事件(綁定的方法)執(zhí)行 const emit = function emit(name, ...params) { if (typeof name !== 'string') throw new TypeError('name is not a string') let arr = listeners[name] if (!Array.isArray(arr)) return for (let i = 0; i < arr.length; i++) { let callback = arr[i] if (typeof callback !== 'function') { arr.splice(i, 1) i-- continue } callback(...params) } } /* 轉(zhuǎn)移“_”的使用權(quán) */ let origin = null const noConflict = function noConflict() { if (window._ === utils) { window._ = origin } return utils } /* 暴露API */ const utils = { isType, isObject, isArray, isArrayLike, isWindow, isFunction, isPlainObject, isEmptyObject, isNumeric, noConflict, each, storage, formatTime, define, delay, on, off, emit } if (typeof noGlobal === "undefined") { origin = window._ window.utils = window._ = utils } return utils } );
import { cloneDeep } from "lodash"; // import colneDeep from "lodash/colneDeep"; import _ from "@/assets/utils";//這個(gè)是寫的公共方法。 // createStore:創(chuàng)建store容器的。 export const createStore = function createStore(reducer) { if (typeof reducer !== "function") { throw new TypeError(`reducer必須是一個(gè)函數(shù)`); } // 公共狀態(tài) let state; //未來(lái)state是對(duì)象 0x001 // 事件池 let listeners = []; // 獲取公共狀態(tài) const getState = function getState() { // redux源碼本身存在一個(gè)bug:基于getstate獲取的公共狀態(tài)信息,和容器中的state是相同的堆內(nèi)存地址。這樣在組件中,當(dāng)我們獲取公共狀態(tài)后,可以繞過(guò)dispatch派發(fā),直接通過(guò)state.xxx=xxx修改公共狀態(tài)信息! // return state // 所以我們把返回的狀態(tài)信息,最好進(jìn)行深拷貝。 // 弊端:浪費(fèi)性能。 return cloneDeep(state); }; //向事件池中加入方法。 const subscribe = function subscribe(callback) { if (typeof callback !== "function") { throw new TypeError(`callback必須是函數(shù)`); } if (!listeners.includes(callback)) { listeners.push(callback); } //返回從事件池中移除函數(shù)的方法。 return function unsubscribe() { let index = listeners.indexOf(callback); if (index >= 0) { listeners.splice(index, 1); } }; }; // 任務(wù)派發(fā),通知reducer執(zhí)行。 const dispatch = function dispatch(action) { console.log(`111-->`, 111); if (!_.isPlainObject(action)) { throw new TypeError(`action必須是一個(gè)標(biāo)準(zhǔn)對(duì)象`); } if (!("type" in action)) { throw new TypeError(`action對(duì)象必須具備type屬性`); } // 通知reducer執(zhí)行: 修改公共狀態(tài); state = reducer(state, action); // 公共狀態(tài)更改,我們需要通知事件池中的方法執(zhí)行! console.log(`1listeners-->`, listeners); let arr = listeners.slice()//防止事件執(zhí)行時(shí),更改事件池,造成事件池?cái)?shù)組塌陷。 arr.forEach((listener) => { if (typeof listener === "function") { listener(); console.log(`2listener-->`, listener); } }); }; // 最開(kāi)始默認(rèn):我們需要派發(fā)第一次,其目的是設(shè)置初始狀態(tài)信息。 dispatch({ type: Symbol("init-state初始化的類型") }); // 返回store對(duì)象 return { getState, subscribe, dispatch, }; };
import { cloneDeep } from "lodash";
import _ from "@/assets/utils";
/* createStore:創(chuàng)建store容器的 */
export const createStore = function createStore(reducer) {
if (typeof reducer !== "function")
throw new TypeError(`reducer必須是一個(gè)函數(shù)`);
// 公共狀態(tài)
let state;
// 事件池
let listeners = [];
// 獲取公共狀態(tài)
const getState = function getState() {
// redux源碼本身存在一個(gè)BUG:基于getState獲取的公共狀態(tài)信息,和容器中的state是相同的堆內(nèi)存,這樣在組件中,當(dāng)我們獲取公共狀態(tài)后,可以繞過(guò)dispatch派發(fā),直接通過(guò)state.xxx=xxx修改公共狀態(tài)信息!
// return state
// 所以我們把返回的狀態(tài)信息,最好進(jìn)行深拷貝「弊端:浪費(fèi)性能」
return cloneDeep(state);
};
// 向事件池中加入方法
const subscribe = function subscribe(callback) {
if (typeof callback !== "function")
throw new TypeError("callback必須是函數(shù)");
if (!listeners.includes(callback)) {
listeners.push(callback);
}
// 返回從事件池中移除函數(shù)的方法
return function unsubscribe() {
let index = listeners.indexOf(callback);
if (index >= 0) {
listeners.splice(index, 1);
}
};
};
// 任務(wù)派發(fā),通知reducer執(zhí)行
const dispatch = function dispatch(action) {
if (!_.isPlainObject(action))
throw new TypeError("action必須是一個(gè)標(biāo)準(zhǔn)對(duì)象");
if (!("type" in action)) throw new TypeError("action對(duì)象必須具備type屬性");
// 通知reducer執(zhí)行:修改公共狀態(tài)
state = reducer(state, action);
// 公共狀態(tài)更改,我們需要通知事件池中的方法執(zhí)行
let arr = listeners.slice();
arr.forEach((listener) => {
if (typeof listener === "function") listener();
});
};
// 最開(kāi)始默認(rèn):我們需要派發(fā)第一次,其目的是設(shè)置初始狀態(tài)信息
dispatch({
type: Symbol("INIT-STATE"),
});
// 返回store對(duì)象
return {
getState,
subscribe,
dispatch,
};
};
redux真實(shí)源碼解讀
-
redux插件存在的幾個(gè)問(wèn)題:
-
執(zhí)行 store.getState() 獲取的公共狀態(tài),和容器中的公共狀態(tài),使用的是相同的堆內(nèi)存地址!
- 問(wèn)題:這樣我在組件中,可以基于獲取的狀態(tài),直接通過(guò) state.xxx=xxx 就修改了容器中的狀態(tài)信息,相當(dāng)于繞過(guò)了 disptach 也可以直接修改狀態(tài)「這樣不利于狀態(tài)的統(tǒng)一管理」!
-
向事件池中加入 “讓組件更新的函數(shù)” 的時(shí)候,并沒(méi)有做去重的處理!
-
在redux插件中,組件中只要用到了公共狀態(tài),就需要把組件更新的辦法放在事件池中;后期不論哪個(gè)狀態(tài)發(fā)生改變,事件池中所有的方法都會(huì)執(zhí)行(所有組件都會(huì)更新),即便當(dāng)前組件并沒(méi)有用到修改的這個(gè)狀態(tài)!!這樣導(dǎo)致很多不必要的性能消耗?。?/p>
- 在大的機(jī)制改變不了的情況下,我們盡可能做到:
- 組件第一次渲染完畢,向事件池中加入讓組件更新的辦法??!
- 當(dāng)組件銷毀的時(shí)候,一定要把方法從事件池中移除掉??!
-
-
/node_modules/redux/dist/redux.js
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Redux = {})); }(this, (function (exports) { 'use strict'; // Inlined version of the `symbol-observable` polyfill var $$observable = (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })(); /** * These are private action types reserved by Redux. * For any unknown actions, you must return the current state. * If the current state is undefined, you must return the initial state. * Do not reference these action types directly in your code. */ var randomString = function randomString() { return Math.random().toString(36).substring(7).split('').join('.'); }; var ActionTypes = { INIT: "@@redux/INIT" + randomString(), REPLACE: "@@redux/REPLACE" + randomString(), PROBE_UNKNOWN_ACTION: function PROBE_UNKNOWN_ACTION() { return "@@redux/PROBE_UNKNOWN_ACTION" + randomString(); } }; /** * @param {any} obj The object to inspect. * @returns {boolean} True if the argument appears to be a plain object. */ function isPlainObject(obj) { if (typeof obj !== 'object' || obj === null) return false; var proto = obj; while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto); } return Object.getPrototypeOf(obj) === proto; } // Inlined / shortened version of `kindOf` from https://github.com/jonschlinkert/kind-of function miniKindOf(val) { if (val === void 0) return 'undefined'; if (val === null) return 'null'; var type = typeof val; switch (type) { case 'boolean': case 'string': case 'number': case 'symbol': case 'function': { return type; } } if (Array.isArray(val)) return 'array'; if (isDate(val)) return 'date'; if (isError(val)) return 'error'; var constructorName = ctorName(val); switch (constructorName) { case 'Symbol': case 'Promise': case 'WeakMap': case 'WeakSet': case 'Map': case 'Set': return constructorName; } // other return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); } function ctorName(val) { return typeof val.constructor === 'function' ? val.constructor.name : null; } function isError(val) { return val instanceof Error || typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'; } function isDate(val) { if (val instanceof Date) return true; return typeof val.toDateString === 'function' && typeof val.getDate === 'function' && typeof val.setDate === 'function'; } function kindOf(val) { var typeOfVal = typeof val; { typeOfVal = miniKindOf(val); } return typeOfVal; } /** * @deprecated * * **We recommend using the `configureStore` method * of the `@reduxjs/toolkit` package**, which replaces `createStore`. * * Redux Toolkit is our recommended approach for writing Redux logic today, * including store setup, reducers, data fetching, and more. * * **For more details, please read this Redux docs page:** * **https://redux.js.org/introduction/why-rtk-is-redux-today** * * `configureStore` from Redux Toolkit is an improved version of `createStore` that * simplifies setup and helps avoid common bugs. * * You should not be using the `redux` core package by itself today, except for learning purposes. * The `createStore` method from the core `redux` package will not be removed, but we encourage * all users to migrate to using Redux Toolkit for all Redux code. * * If you want to use `createStore` without this visual deprecation warning, use * the `legacy_createStore` import instead: * * `import { legacy_createStore as createStore} from 'redux'` * */ function createStore(reducer, preloadedState, enhancer) { var _ref2; if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3] === 'function') { throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'); } if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState; preloadedState = undefined; } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error("Expected the enhancer to be a function. Instead, received: '" + kindOf(enhancer) + "'"); } return enhancer(createStore)(reducer, preloadedState); } if (typeof reducer !== 'function') { throw new Error("Expected the root reducer to be a function. Instead, received: '" + kindOf(reducer) + "'"); } var currentReducer = reducer;//當(dāng)前管理函數(shù)。 var currentState = preloadedState;//初始狀態(tài)值。 var currentListeners = [];//當(dāng)前事件池。 var nextListeners = currentListeners;//下次的事件池。 var isDispatching = false;//是否正在派發(fā)事件中,如果正在派發(fā),就暫停執(zhí)行一些操作。 /** * This makes a shallow copy of currentListeners so we can use * nextListeners as a temporary list while dispatching. * * This prevents any bugs around consumers calling * subscribe/unsubscribe in the middle of a dispatch. */ // 讓 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice(); } } /** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */ // 獲取狀態(tài)。 function getState() { if (isDispatching) { throw new Error('You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.'); } return currentState; } /** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * You may call `dispatch()` from a change listener, with the following * caveats: * * 1. The subscriptions are snapshotted just before every `dispatch()` call. * If you subscribe or unsubscribe while the listeners are being invoked, this * will not have any effect on the `dispatch()` that is currently in progress. * However, the next `dispatch()` call, whether nested or not, will use a more * recent snapshot of the subscription list. * * 2. The listener should not expect to see all state changes, as the state * might have been updated multiple times during a nested `dispatch()` before * the listener is called. It is, however, guaranteed that all subscribers * registered before the `dispatch()` started will be called with the latest * state by the time it exits. * * @param {Function} listener A callback to be invoked on every dispatch. * @returns {Function} A function to remove this change listener. */ // 事件訂閱事件。 function subscribe(listener) { if (typeof listener !== 'function') { throw new Error("Expected the listener to be a function. Instead, received: '" + kindOf(listener) + "'"); } if (isDispatching) { throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.'); } var isSubscribed = true;//是否正在訂閱中。 ensureCanMutateNextListeners();//防止數(shù)組數(shù)據(jù)塌陷。 nextListeners.push(listener); return function unsubscribe() { if (!isSubscribed) { return; } if (isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.'); } isSubscribed = false; ensureCanMutateNextListeners(); var index = nextListeners.indexOf(listener); nextListeners.splice(index, 1); currentListeners = null; }; } /** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * * @returns {Object} For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ // 派發(fā)事件。 function dispatch(action) { if (!isPlainObject(action)) { throw new Error("Actions must be plain objects. Instead, the actual type was: '" + kindOf(action) + "'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples."); } if (typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'); } if (isDispatching) { throw new Error('Reducers may not dispatch actions.'); } try { isDispatching = true; currentState = currentReducer(currentState, action); } finally { isDispatching = false; } var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener();//內(nèi)部執(zhí)行時(shí),如果用unsubscribe()改變了事件池,但 } return action; } /** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * @param {Function} nextReducer The reducer for the store to use instead. * @returns {void} */ function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error("Expected the nextReducer to be a function. Instead, received: '" + kindOf(nextReducer)); } currentReducer = nextReducer; // This action has a similiar effect to ActionTypes.INIT. // Any reducers that existed in both the new and old rootReducer // will receive the previous state. This effectively populates // the new state tree with any relevant data from the old one. dispatch({ type: ActionTypes.REPLACE }); } /** * Interoperability point for observable/reactive libraries. * @returns {observable} A minimal observable of state changes. * For more information, see the observable proposal: * https://github.com/tc39/proposal-observable */ function observable() { var _ref; var outerSubscribe = subscribe; return _ref = { /** * The minimal observable subscription method. * @param {Object} observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns {subscription} An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */ subscribe: function subscribe(observer) { if (typeof observer !== 'object' || observer === null) { throw new Error("Expected the observer to be an object. Instead, received: '" + kindOf(observer) + "'"); } function observeState() { if (observer.next) { observer.next(getState()); } } observeState(); var unsubscribe = outerSubscribe(observeState); return { unsubscribe: unsubscribe }; } }, _ref[$$observable] = function () { return this; }, _ref; } // When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. dispatch({ type: ActionTypes.INIT }); return _ref2 = { dispatch: dispatch, subscribe: subscribe, getState: getState, replaceReducer: replaceReducer }, _ref2[$$observable] = observable, _ref2; } /** * Creates a Redux store that holds the state tree. * * **We recommend using `configureStore` from the * `@reduxjs/toolkit` package**, which replaces `createStore`: * **https://redux.js.org/introduction/why-rtk-is-redux-today** * * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any} [preloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param {Function} [enhancer] The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */ var legacy_createStore = createStore; /** * Prints a warning in the console if it exists. * * @param {String} message The warning message. * @returns {void} */ function warning(message) { /* eslint-disable no-console */ if (typeof console !== 'undefined' && typeof console.error === 'function') { console.error(message); } /* eslint-enable no-console */ try { // This error was thrown as a convenience so that if you enable // "break on all exceptions" in your console, // it would pause the execution at this line. throw new Error(message); } catch (e) {} // eslint-disable-line no-empty } function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) { var reducerKeys = Object.keys(reducers); var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer'; if (reducerKeys.length === 0) { return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.'; } if (!isPlainObject(inputState)) { return "The " + argumentName + " has unexpected type of \"" + kindOf(inputState) + "\". Expected argument to be an object with the following " + ("keys: \"" + reducerKeys.join('", "') + "\""); } var unexpectedKeys = Object.keys(inputState).filter(function (key) { return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]; }); unexpectedKeys.forEach(function (key) { unexpectedKeyCache[key] = true; }); if (action && action.type === ActionTypes.REPLACE) return; if (unexpectedKeys.length > 0) { return "Unexpected " + (unexpectedKeys.length > 1 ? 'keys' : 'key') + " " + ("\"" + unexpectedKeys.join('", "') + "\" found in " + argumentName + ". ") + "Expected to find one of the known reducer keys instead: " + ("\"" + reducerKeys.join('", "') + "\". Unexpected keys will be ignored."); } } function assertReducerShape(reducers) { Object.keys(reducers).forEach(function (key) { var reducer = reducers[key]; var initialState = reducer(undefined, { type: ActionTypes.INIT }); if (typeof initialState === 'undefined') { throw new Error("The slice reducer for key \"" + key + "\" returned undefined during initialization. " + "If the state passed to the reducer is undefined, you must " + "explicitly return the initial state. The initial state may " + "not be undefined. If you don't want to set a value for this reducer, " + "you can use null instead of undefined."); } if (typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === 'undefined') { throw new Error("The slice reducer for key \"" + key + "\" returned undefined when probed with a random type. " + ("Don't try to handle '" + ActionTypes.INIT + "' or other actions in \"redux/*\" ") + "namespace. They are considered private. Instead, you must return the " + "current state for any unknown actions, unless it is undefined, " + "in which case you must return the initial state, regardless of the " + "action type. The initial state may not be undefined, but can be null."); } }); } /** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */ function combineReducers(reducers) { // 把傳遞過(guò)來(lái)的reducers對(duì)象,淺拷貝把合理的reducer項(xiàng)拷貝一份給finalReducers。 var reducerKeys = Object.keys(reducers);//各個(gè)reducer。 var finalReducers = {}; for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i]; { if (typeof reducers[key] === 'undefined') { warning("No reducer provided for key \"" + key + "\""); } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key]; } } // 拿到各個(gè)key。 var finalReducerKeys = Object.keys(finalReducers); // This is used to make sure we don't warn about the same // keys multiple times. var unexpectedKeyCache; { unexpectedKeyCache = {}; } var shapeAssertionError; try { assertReducerShape(finalReducers); } catch (e) { shapeAssertionError = e; } return function combination(state, action) { if (state === void 0) { state = {}; } if (shapeAssertionError) { throw shapeAssertionError; } { var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache); if (warningMessage) { warning(warningMessage); } } var hasChanged = false; var nextState = {}; for (var _i = 0; _i < finalReducerKeys.length; _i++) { var _key = finalReducerKeys[_i]; var reducer = finalReducers[_key]; var previousStateForKey = state[_key]; var nextStateForKey = reducer(previousStateForKey, action); if (typeof nextStateForKey === 'undefined') { var actionType = action && action.type; throw new Error("When called with an action of type " + (actionType ? "\"" + String(actionType) + "\"" : '(unknown type)') + ", the slice reducer for key \"" + _key + "\" returned undefined. " + "To ignore an action, you must explicitly return the previous state. " + "If you want this reducer to hold no value, you can return null instead of undefined."); } nextState[_key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length; return hasChanged ? nextState : state; }; } function bindActionCreator(actionCreator, dispatch) { return function () { return dispatch(actionCreator.apply(this, arguments)); }; } /** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. * * For convenience, you can also pass an action creator as the first argument, * and get a dispatch wrapped function in return. * * @param {Function|Object} actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function. * * @param {Function} dispatch The `dispatch` function available on your Redux * store. * * @returns {Function|Object} The object mimicking the original object, but with * every action creator wrapped into the `dispatch` call. If you passed a * function as `actionCreators`, the return value will also be a single * function. */ function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error("bindActionCreators expected an object or a function, but instead received: '" + kindOf(actionCreators) + "'. " + "Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?"); } var boundActionCreators = {}; for (var key in actionCreators) { var actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } return boundActionCreators; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } /** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ function compose() { for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } if (funcs.length === 0) { return function (arg) { return arg; }; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce(function (a, b) { return function () { return a(b.apply(void 0, arguments)); }; }); } /** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ function applyMiddleware() { for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } return function (createStore) { return function () { var store = createStore.apply(void 0, arguments); var _dispatch = function dispatch() { throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.'); }; var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(void 0, arguments); } }; var chain = middlewares.map(function (middleware) { return middleware(middlewareAPI); }); _dispatch = compose.apply(void 0, chain)(store.dispatch); return _objectSpread2(_objectSpread2({}, store), {}, { dispatch: _dispatch }); }; }; } exports.__DO_NOT_USE__ActionTypes = ActionTypes; exports.applyMiddleware = applyMiddleware; exports.bindActionCreators = bindActionCreators; exports.combineReducers = combineReducers; exports.compose = compose; exports.createStore = createStore; exports.legacy_createStore = legacy_createStore; Object.defineProperty(exports, '__esModule', { value: true }); })));
redux工程化開(kāi)發(fā)
-
所謂的redux工程化開(kāi)發(fā),其實(shí)就是在大型項(xiàng)目中,按照模塊,分別管理每個(gè)模塊下的 狀態(tài)和reducer。
-
工程化開(kāi)發(fā)步驟:
- 拆分和合并reducer。
- 第一步工程化的意義:
- 公共狀態(tài)按照模塊進(jìn)行管理,防止各個(gè)模塊下的狀態(tài)沖突。
- reducer修改狀態(tài)的邏輯,也拆分到各個(gè)模塊下了,方便開(kāi)發(fā)和維護(hù),以及團(tuán)隊(duì)協(xié)作!
- 即修改東西,不用到一個(gè)js文件上修改了,更不容易造成沖突。
- 第一步工程化的意義:
- 拆分和合并reducer。
combineReducers底層處理的機(jī)制
- combineReducers底層處理的機(jī)制
-
首先store容器中的公共狀態(tài),會(huì)按照設(shè)定的各個(gè)模塊名,分別管理各模塊下的狀態(tài)。
const reducer = combineReducers({ 模塊名: 對(duì)應(yīng)的reducer, }); state={ vote:{ title, supNum, oppNum, }, task:{ title, list, } }
- 這樣我們?cè)倩趕tore.getState()獲取的就是總狀態(tài)了,想獲取具體的信息,還需要找到各個(gè)模塊,再去訪問(wèn)處理!
-
每一次dispatch派發(fā)的時(shí)候,會(huì)把所有模塊的reducer都執(zhí)行一遍。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-464456.html
-
自定義hook
- 新建一個(gè)以u(píng)se開(kāi)頭的駝峰命名法的函數(shù)。
- 自定義hook函數(shù)內(nèi)部可以使用各種hook鉤子,如useState()與useEffect()。
- 自定義hook函數(shù);
-
/src/useForceUpdate.js
- hook函數(shù):import { useState, useEffect } from "react"; export default function useForceUpdate(store) { let [, setRandom] = useState(+new Date()); // 但這個(gè)useEffect還得等后期組件渲染完成后才執(zhí)行內(nèi)部的函數(shù)。 let unsubscribe; useEffect(() => { // 組件第一次渲染完畢,把讓組件更新的辦法放在事件池中。 // 執(zhí)行subscribe會(huì)返回unsubscribe,目的是用于移除剛才加入事件池中的方法。 unsubscribe = store.subscribe(() => { setRandom(+new Date()); }); return () => { // 組件銷毀的時(shí)候,把放在事件池中的方法移除掉。 unsubscribe(); unsubscribe = null; }; }, []); // 手動(dòng)返回一個(gè)remove函數(shù),remove可以通過(guò)閉包,訪問(wèn)到當(dāng)前作用域中的unsubscribe,當(dāng)組件渲染之后,unsubscribe就是useEffect中賦值的值。 // 手動(dòng)返回一個(gè)remove函數(shù): 等待后期。 return function remove() { if (unsubscribe) { unsubscribe(); } }; }
進(jìn)階參考
到了這里,關(guān)于20230529----重返學(xué)習(xí)-復(fù)合組件通信redux-redux源碼-redux工程化開(kāi)發(fā)-自定義hook的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!