自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

理解了狀態(tài)管理,就理解了前端開發(fā)的核心?

開發(fā) 前端
狀態(tài)就是數(shù)據(jù)的變化。前端應(yīng)用的核心問題就是管理狀態(tài),管理狀態(tài)變化之前的通過視圖或者其他方式觸發(fā)的異步過程,管理狀態(tài)變化之后的聯(lián)動渲染和聯(lián)動的邏輯執(zhí)行。

狀態(tài)管理是前端整天遇到的概念,但是大家是否思考過什么是狀態(tài),管理的又是什么呢?

我們知道,程序是處理數(shù)據(jù)的,數(shù)據(jù)是信息的載體,比如顏色是紅色或藍(lán)色這就是數(shù)據(jù)。

那為什么不叫數(shù)據(jù)管理呢?狀態(tài)和數(shù)據(jù)是什么關(guān)系?

什么是狀態(tài)

狀態(tài)是數(shù)據(jù)的變化,比如顏色是紅色或藍(lán)色是數(shù)據(jù),而顏色從紅色變?yōu)樗{(lán)色這就是狀態(tài)了。

狀態(tài)的改變對應(yīng)著視圖的渲染或者某段邏輯的執(zhí)行。比如顏色從紅色變?yōu)樗{(lán)色可能就要重新渲染視圖,并且執(zhí)行發(fā)送請求到服務(wù)端的邏輯。

通過視圖交互或者其他方式觸發(fā)狀態(tài)的變化,狀態(tài)變化聯(lián)動視圖的渲染和邏輯的執(zhí)行,這就是前端應(yīng)用的核心。

為什么之前 jQuery 時代沒咋聽說狀態(tài)管理的概念,而 Vue、React 時代經(jīng)常聽到呢?

jQuery 時代是手動把數(shù)據(jù)渲染到視圖和執(zhí)行數(shù)據(jù)變化之后的邏輯的,它可能沒有明確的狀態(tài)這一層,而是直接把數(shù)據(jù)渲染成 dom,下次需要數(shù)據(jù)也是從 dom 來取的。

而 Vue、React 前端框架的時代不需要手動操作 dom 和執(zhí)行數(shù)據(jù)變化之后的邏輯,只要管理好狀態(tài),由前端框架負(fù)責(zé)狀態(tài)變化之后的處理。

狀態(tài)管理管理的是什么呢?

什么是狀態(tài)管理

狀態(tài)管理具體有兩層含義:

  • 狀態(tài)變化之前的邏輯,一般是異步的。
  • 狀態(tài)變化之后的聯(lián)動處理,比如渲染視圖或執(zhí)行某段邏輯。

比如 React 的 setState 不會馬上修改狀態(tài),而是異步的批量的執(zhí)行,把狀態(tài)做一下合并。

比如 Redux 的 action 在修改全局 state 之前也是要經(jīng)歷中間件的處理的。

這些都是狀態(tài)變化之前的異步過程的管理,是狀態(tài)管理的第一層含義。

再比如 React setState 修改了狀態(tài)之后要觸發(fā)視圖的渲染和生命周期函數(shù)的執(zhí)行,hooks 在依賴數(shù)組的狀態(tài)變化之后也會重新執(zhí)行。(vue 的 data 修改之后會重新渲染視圖、執(zhí)行 computed 和 watch 邏輯)

Redux 修改了全局狀態(tài)之后要通知組件做渲染或者做其他邏輯的處理,Vuex、Mobx 等都是。

這些是狀態(tài)變化之后的聯(lián)動處理的管理,是狀態(tài)管理的第二層含義。

我們知道了什么是狀態(tài),什么是狀態(tài)管理,那前端框架 Vue、React 和全局狀態(tài)管理的庫 Redux、Mobx、Vuex 都是怎么實現(xiàn)狀態(tài)管理的呢?

狀態(tài)管理的兩種實現(xiàn)思路

狀態(tài)不會是一個,多個狀態(tài)的集合會用對象的 key、value 來表示,比如 React 的 state 對象,Vue 的 data 對象(雖然叫 data 也是指的狀態(tài))。

怎么監(jiān)聽一個對象的變化呢?

我們是不是可以提供一個 api 來修改,在這個 api 內(nèi)做 state 變化之前的處理,并且在 state 變化之后做聯(lián)動處理。

這樣的方案只能通過 api 觸發(fā)狀態(tài)修改,直接修改 state 是觸發(fā)不了狀態(tài)管理邏輯的。

React 的 setState 就是這種思路,通過 setState 修改狀態(tài)會做狀態(tài)變化之前的批量異步的狀態(tài)合并,會觸發(fā)狀態(tài)變化之后視圖渲染和 hooks、生命周期的重新執(zhí)行。但是直接修改 state 是沒用的。

那怎么讓直接修改狀態(tài)也能監(jiān)聽到變化呢?

可以對狀態(tài)對象做一層代理,代理它的 get、set,當(dāng)執(zhí)行狀態(tài)的 get 的時候把依賴該狀態(tài)的邏輯收集起來,當(dāng) set 修改狀態(tài)的時候通知所有依賴它的邏輯(視圖渲染、邏輯執(zhí)行)做更新。

Vue 的 data 監(jiān)聽變化就是用的這種思路,在狀態(tài) get 的時候把依賴封裝成 Watcher,當(dāng) set 的時候通知所有 Watcher 做更新。

這種思路叫做響應(yīng)式(reactive),也就是狀態(tài)變化之后自動響應(yīng)變化做聯(lián)動處理的意思。

代理 get、set 可以用 Object.defineProperty 的 api,但是它不能監(jiān)聽動態(tài)增刪的對象屬性,所以 Vue3 改為了用 Proxy 的 api 實現(xiàn)。

監(jiān)聽對象的變化就這兩種方式:

  • 提供 api 來修改,內(nèi)部做聯(lián)動處理。
  • 對對象做一層代理,set 的時候做聯(lián)動處理,通知 get 時收集的所有依賴。

前端框架狀態(tài)變化的性能優(yōu)化

但是頻繁的修改 state 不是每次都要做聯(lián)動處理,有一些可以合并的,比如兩次都把顏色改為紅色,那后續(xù)邏輯就沒必要執(zhí)行兩次,需要再做些性能方面的優(yōu)化。

所以 React 的 setState 是異步的,會做批量的 state 合并(注意,React 的 setState 傳入的不是最終的 state,而是 state 的 diff,React 內(nèi)部去把這些 diff state 更新到 state)。

而 Vue 因為是直接修改的同一個對象,所以沒必要做啥合并,它的 Watcher 執(zhí)行是異步的,對多次放到隊列里的 Watcher 做下去重就行了。

組件間的狀態(tài)管理

組件內(nèi)的狀態(tài)管理就是這樣的,利用前端框架自帶的 state 機(jī)制來管理。

那組件之間呢?一個組件的 state 變了如何聯(lián)動其他組件變化?

props

通過 props,把當(dāng)前組件的 state 作為 props 傳入其他組件就行了,這樣就能聯(lián)動變化。

但是 props 只能一層層傳遞,如果組件和想聯(lián)動變化的組件相隔很多層,傳遞 props 就很麻煩。

這種情況下前端框架也都提供了解決方案,React 提供了 Context、Vue 提供了 Event Bus。

Context、Event Bus

React 組件可以在 context 中存放 state,當(dāng) context 中的 state 變化的時候會直接觸發(fā)關(guān)聯(lián)組件的渲染。

Vue 可以在一個組件內(nèi) emit 一個事件,然后另一個組件 on 這個事件,然后更新自己的 data 來觸發(fā)渲染。不過這兩個 api 在 Vue3 都廢棄了。

這種前端框架自帶的任意層組件的狀態(tài)聯(lián)動方案只能處理簡單的場景,復(fù)雜的場景還是得用全局狀態(tài)管理庫,比如 Redux、Vuex、Mobx 這些。

為什么這么說呢?

還記得狀態(tài)管理的兩層含義么?狀態(tài)變化前的異步過程的管理,狀態(tài)變化后的聯(lián)動處理。

Context 和 Event Bus 都只做到了狀態(tài)變化后的聯(lián)動處理,但是沒有對狀態(tài)變化前的異步過程管理做支持。

比如多個組件都要修改 context 中的值(或者通過 event bus 修改全局狀態(tài)),這個過程都要執(zhí)行一段異步邏輯,要做 loading 的展示,那多個組件里怎么復(fù)用這段 loading 的邏輯呢?

還有,如果異步過程比較麻煩,需要用 rxjs 這樣的庫,用 context 和 event bus 的方案怎么和 rxjs 結(jié)合呢?

當(dāng)然,是可以對 context 和 event bus 做一些邏輯復(fù)用的封裝和一些結(jié)合 rxjs 方案之類的封裝的,但是比較麻煩。

而且更重要的是如果你想做這些的時候,那也就沒必要用 context 和 event bus 了,直接用全局狀態(tài)管理庫就行。

Redux、Mobx、Vuex

redux 就提供了中間件的機(jī)制,組件里發(fā)送 action 到 store(存放全局 state 的地方),之前會經(jīng)歷層層中間件的處理,在這里就可以做一些可復(fù)用的邏輯的封裝,比如 loading 的處理,也可以結(jié)合 rxjs 這種異步過程處理方案。

redux 里最常用的中間件就是 redux-saga 和 redux-observable 了,這倆都是做異步過程的管理的。

redux-saga 是基于 generator 實現(xiàn)的,不管是同步還是異步,都只要聲明式的描述要執(zhí)行的邏輯就行,由 saga 內(nèi)部的執(zhí)行器會去做同步或異步的處理,描述異步邏輯就很簡潔,而且 redux-saga 提供了很多內(nèi)置的邏輯封裝。

redux-observable 則是結(jié)合 rxjs 的方案了,把 action 變成數(shù)據(jù)源,經(jīng)歷層層 opreator 的處理,最后傳遞到 store。可以用 rxjs 生態(tài)大量的 oprator,做下組裝就行,根本不用自己寫異步邏輯的具體實現(xiàn)。

vuex 也同樣支持中間件機(jī)制。

mobx 沒有提供中間件機(jī)制,它的 action 是執(zhí)行狀態(tài) class 的某個方法,可以用 class 的那套來做封裝。

有的同學(xué)對這些狀態(tài)管理庫不太熟,簡單來介紹下。

我們理清了狀態(tài)管理的實現(xiàn)只有兩種方案,一種是提供 api 做修改,一種是對 state 對象做響應(yīng)式代理。

前端框架的狀態(tài)管理是這樣,獨立的全局狀態(tài)管理庫也同樣是這樣。

redux 就是提供 api 來修改的方案,通過 reducer 函數(shù)來對傳入的 action 做處理,返回新的 state。

而且 redux 這種思路是函數(shù)式的思想,每個 reducer 都是輸入和輸出一一對應(yīng)的純函數(shù),返回的 state 都是全新的,為了方便創(chuàng)建新的 state,一般會搭配 immutable 庫,只要修改屬性就會返回新的 state 對象。

mobx 是響應(yīng)式代理的方案,它對全局 state 做了一層代理(通過 Object.defineProperty),狀態(tài)的 get 收集依賴,set 的時候觸發(fā)依賴更新。

所以這種方案很自然的可以把全局 state 組織成一個個 class,是面向?qū)ο蟮乃枷?,可以通過繼承等方式實現(xiàn)邏輯復(fù)用。

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {observer} from 'mobx-react';

@observer
class TodoListView extends Component {
render() {
return <div>
<ul>
{this.props.todoList.todos.map(todo =>
<TodoView todo={todo} key={todo.id} />
)}
</ul>
Tasks left: {this.props.todoList.unfinishedTodoCount}
</div>
}
}

const TodoView = observer(({todo}) =>
<li>
<input
type="checkbox"
checked={todo.finished}
onClick={() => todo.finished = !todo.finished}
/>{todo.title}
</li>
)

const store = new TodoList();
ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount'));

vuex 則像是兩種思路的結(jié)合,內(nèi)部是用響應(yīng)式代理來實現(xiàn)的變化監(jiān)聽,但是暴露出的 api 卻是 redux 的 action 那一套。

和 React 搭配使用的話,需要把組件添加到狀態(tài)的依賴中,這個不用自己調(diào)用 subscribe 之類的 api,直接用一些封裝好的高階組件(接受組件作為參數(shù)返回新的組件的組件)就行,比如 react-redux 的 connect,mobx-react 的 observer。

總結(jié)

講了這么多,回過頭來看一下就會發(fā)現(xiàn):

不管是前端框架內(nèi)置的組件內(nèi)狀態(tài)變化管理的方案(react 的 setState、vue 的直接修改 data),還是前端框架提供的組件間的狀態(tài)管理方案(props、react 的 context、vue 的 event bus),或是第三方的全局狀態(tài)管理方案(redux、vuex、mobx 等),都沒有脫離那兩種實現(xiàn)狀態(tài)管理的方式:提供修改狀態(tài)的 api 或者對狀態(tài)對象做一層響應(yīng)式代理。也沒有脫離狀態(tài)管理的兩層含義:對狀態(tài)變化前的異步過程做管理,狀態(tài)變化后做聯(lián)動處理。只不過它們用在了不同的地方(前端框架內(nèi)、全局狀態(tài)管理庫),提供了不同的封裝形式(對象、函數(shù)),基于不同的思想(函數(shù)式、面向?qū)ο?結(jié)合了不同的異步管理方案(rxjs、generator + 自定義執(zhí)行器)。

所以,狀態(tài)就是數(shù)據(jù)的變化。前端應(yīng)用的核心問題就是管理狀態(tài),管理狀態(tài)變化之前的通過視圖或者其他方式觸發(fā)的異步過程,管理狀態(tài)變化之后的聯(lián)動渲染和聯(lián)動的邏輯執(zhí)行。

雖然我們會用不同的前端框架,不同的全局狀態(tài)管理庫,結(jié)合不同的異步過程處理方案,但是思想都是一樣的。

毫不夸張地說,理解了狀態(tài)管理,就理解了前端開發(fā)的核心。

責(zé)任編輯:武曉燕 來源: 神光的編程秘籍
相關(guān)推薦

2020-04-16 10:55:03

Java虛擬機(jī)字節(jié)碼

2022-03-27 09:06:25

vuexActionsMutations

2024-03-15 08:23:26

異步編程函數(shù)

2019-09-29 06:12:38

交換機(jī)配置vlan

2019-12-26 09:15:44

網(wǎng)絡(luò)IOLinux

2012-11-30 11:19:02

JavaScript

2024-11-25 07:39:48

2022-10-20 18:43:32

C語言golang安全

2018-03-21 16:19:40

MVCMVPMVVM

2022-07-27 22:59:53

Node.jsNest

2019-09-16 08:32:59

遞歸算法編程

2019-09-18 10:12:37

遞歸數(shù)據(jù)結(jié)構(gòu)

2021-09-07 07:55:22

Linux CPULinux 系統(tǒng)

2022-11-26 00:22:14

引用類型數(shù)組

2019-05-28 09:40:39

TCP協(xié)議socket接口

2022-08-05 13:03:09

Python依賴管理代碼

2019-05-17 09:02:19

TCP協(xié)議服務(wù)端

2022-02-21 07:45:29

面向?qū)ο?/a>代碼依賴倒置

2020-11-09 14:30:28

Linux多線程數(shù)據(jù)

2021-03-19 07:59:33

紅黑樹面試數(shù)據(jù)
點贊
收藏

51CTO技術(shù)棧公眾號