欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React全局狀態(tài)管理的三種底層機(jī)制探究

 更新時(shí)間:2021年09月14日 10:11:56   作者:zxg_神說(shuō)要有光  
近兩年前端技術(shù)的發(fā)展如火如荼,大量的前端項(xiàng)目都在使用或轉(zhuǎn)向 Vue 和 React 的陣營(yíng),由前端渲染頁(yè)面的單頁(yè)應(yīng)用占比也越來(lái)越高,這篇文章主要給大家介紹了關(guān)于React全局狀態(tài)管理的三種底層機(jī)制,需要的朋友可以參考下

前言

現(xiàn)代前端框架都是基于組件的方式來(lái)開發(fā)頁(yè)面。按照邏輯關(guān)系把頁(yè)面劃分為不同的組件,分別開發(fā)不同的組件,然后把它們一層層組裝起來(lái),把根組件傳入 ReactDOM.render 或者 vue 的 $mount 方法中,就會(huì)遍歷整個(gè)組件樹渲染成對(duì)應(yīng)的 dom。

組件都支持傳遞一些參數(shù)來(lái)定制,也可以在內(nèi)部保存一些交互狀態(tài),并且會(huì)在參數(shù)和狀態(tài)變化以后自動(dòng)的重新渲染對(duì)應(yīng)部分的 dom。

雖然從邏輯上劃分成了不同的組件,但它們都是同一個(gè)應(yīng)用的不同部分,難免要彼此通信、配合。超過(guò)一層的組件之間如果通過(guò)參數(shù)通信,那么中間那層組件就要透?jìng)鬟@些參數(shù)。而參數(shù)本來(lái)是為了定制組件用的,不應(yīng)該為了通信而添加一些沒意義的參數(shù)。

所以,對(duì)于組件的通信,一般不會(huì)通過(guò)組件參數(shù)的層層傳遞,而是通過(guò)放在全局的一個(gè)地方,雙方都從那里存取的方式。

具體的用于全局狀態(tài)管理的方案可能有很多,但是他們的底層無(wú)外乎三種機(jī)制:props、context、state。

下面,我們分別來(lái)探究一下這三種方式是如何做全局狀態(tài)的存儲(chǔ)和傳遞的。

props

我們可以通過(guò)一個(gè)全局對(duì)象來(lái)中轉(zhuǎn),一個(gè)組件向其中存放數(shù)據(jù),另一個(gè)組件取出來(lái)的方式來(lái)通信。

組件里面寫取 store 中數(shù)據(jù)的代碼比較侵入式,總不能每個(gè)用到 store 的組件都加一段這些代碼吧。我們可以把這些邏輯抽成高階組件,用它來(lái)連接(connect)組件和 store。通過(guò)參數(shù)的方式來(lái)把數(shù)據(jù)注入到組件中,這樣,對(duì)組件來(lái)說(shuō)來(lái)源是透明的。

這就是 react-redux 做的事情:

import { connect } from 'react-redux';

function mapStateToProps(state) {
    return { todos: state.todos }
}
  
function mapDispatchToProps(dispatch) {
    return bindActionCreators({ addTodo }, dispatch)
}
  
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)

此外,redux 還提供了中間件機(jī)制,可以攔截組件發(fā)送給 store 的 action 來(lái)執(zhí)行一系列異步邏輯。

比較流行的中間件有 redux-thunk、redux-saga、redux-obervable,分別支持不同的方式來(lái)寫組織異步流程,封裝和復(fù)用異步邏輯。

類似的其他全局狀態(tài)管理的庫(kù),比如 mobox、reconcil 等,也是通過(guò) props 的方式注入全局的狀態(tài)到組件中。

context

跨層組件通信一定要用第三方的方案么,不是的,react 本身也提供了 context 機(jī)制用于這種通信。

React.createContext 的 api 會(huì)返回 Provider 和 Consumer,分別用于提供 state 和取 state,而且也是通過(guò) props 來(lái)透明的傳入目標(biāo)組件的。(這里的 Consumer 也可以換成 useContext 的 api,作用一樣,class 組件用 Provider,function 組件用 useContext)

看起來(lái)和 redux 的方案基本沒啥區(qū)別,其實(shí)最主要的區(qū)別是 context 沒有執(zhí)行異步邏輯的中間件。

所以 context 這種方案適合沒有異步邏輯的那種全局?jǐn)?shù)據(jù)通信,而 redux 適合組織復(fù)雜的異步邏輯。

案例代碼如下:

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

不知道大家有沒有想過(guò),props、state 改變了,重新渲染組件很正常,context 改變了,又是怎么觸發(fā)渲染的呢?

其實(shí) react 內(nèi)部做了處理,如果改變了 context 的值,那么會(huì)遍歷所有的子組件,找到用到 context 值的組件,觸發(fā)它的更新。

所以,props、state、context 都能夠觸發(fā)重新渲染。

state

redux 和 context 的方案,一個(gè)是第三方的,一個(gè)是內(nèi)置的,都是通過(guò) props 來(lái)傳入值或者通過(guò) hooks 來(lái)取值,但它們都是組件外部的,而 state 是組件內(nèi)部的,怎么通過(guò) state 來(lái)做全局狀態(tài)共享呢?

其實(shí) class 組件的 state 做不到,但是 function 組件的 state 可以,因?yàn)樗峭ㄟ^(guò) useState 的 hooks api 創(chuàng)建的,而 useState 可以抽離到自定義 hooks 里,然后不同的 function 組件里引入來(lái)用。

import React, { useState } from 'react';

const useGlobalState = (initialValue) => {
    const [globalState, setGlobalState] = useState(initialValue);
    return [globalState, setGlobalState];
}

function ComponentA() {
    const [globalState, setGlobalState] = useGlobalState({name: 'aaa'});
    
    setGlobalState({name: bbb});
    return <div>{globalState}</div>
}

function ComponentA() {
    const [globalState, setGlobalState] = useGlobalState({name: 'aaa'});
 
    return <div>{globalState}</div>
}

上面這段代碼可以共享全局狀態(tài)?

確實(shí)不可以,因?yàn)楝F(xiàn)在每個(gè)組件都是在自己的 fiber.memorizedState 中放了一個(gè)新的對(duì)象,修改也是修改各自的。

那把這兩個(gè) useState 的初始值指向同一個(gè)對(duì)象不就行了?

這樣多個(gè)組件之間就可以操作同一份數(shù)據(jù)了。

上面的代碼要做下修改:

let globalVal  = {
    name: ''
}

const useGlobalState = () => {
    const [globalState, setGlobalState] = useState(globalVal);

    function updateGlobalState(val) {
        globalVal = val;
        setGlobalState(val);
    }

    return [globalState, updateGlobalState];
}

這樣,每個(gè)組件創(chuàng)建的 state 都指向同一個(gè)對(duì)象,也能做到全局狀態(tài)的共享。

但這里有個(gè)前提,就是只能修改對(duì)象的屬性,而不能修改對(duì)象本身。

總結(jié)

現(xiàn)在前端頁(yè)面的開發(fā)方式是把頁(yè)面按照邏輯拆成一個(gè)個(gè)組件,分別開發(fā)每一個(gè)組件,然后層層組裝起來(lái),傳入 ReactDOM.render 或者 Vue 的 $mount 來(lái)渲染。

組件可以通過(guò) props 來(lái)定制,通過(guò) state 來(lái)保存交互狀態(tài),這些變了都會(huì)自動(dòng)的重新渲染。除此之外,context 變了也會(huì)找到用到 contxt 數(shù)據(jù)的子組件來(lái)觸發(fā)重新渲染。

組件之間彼此配合,所以難免要通信,props 是用于定制組件的,不應(yīng)該用來(lái)透?jìng)鳑]意義的 props,所以要通過(guò)全局對(duì)象來(lái)中轉(zhuǎn)。

react 本身提供了 context 的方案,createContext 會(huì)返回 Provider 和 Consumer,分別用來(lái)存放和讀取數(shù)據(jù)。在 function 組件中,還可以用 useContext 來(lái)代替 Provider。

context 雖然可以共享全局狀態(tài),但是卻沒有異步邏輯的執(zhí)行機(jī)制,當(dāng)有復(fù)雜的異步邏輯的時(shí)候,還是得用 redux 這種,它提供了中間件機(jī)制用于組織異步流程、封裝復(fù)用異步邏輯,比如 redux-saga 中可以把異步邏輯封裝成 saga 來(lái)復(fù)用。
context 和 redux 都支持通過(guò) props 來(lái)注入數(shù)據(jù)到組件中,這樣對(duì)組件是透明的、無(wú)侵入的。

其實(shí)通過(guò) useState 封裝的 自定義 hooks 也可以通過(guò)把初始值指向同一個(gè)對(duì)象的方式來(lái)達(dá)到全局?jǐn)?shù)據(jù)共享的目的,但是是有限制的,只能修改對(duì)象的屬性,不能修改對(duì)象本身。其實(shí)用這種還不如用 context,只是提一下可以這樣做。

簡(jiǎn)單總結(jié)一下就是:context 和 redux 都可以做全局狀態(tài)管理,一個(gè)是內(nèi)置的,一個(gè)是第三方的,沒有異步邏輯用 context,有異步邏輯用 redux。

到此這篇關(guān)于React全局狀態(tài)管理的三種底層機(jī)制的文章就介紹到這了,更多相關(guān)React全局狀態(tài)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論