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

前端:從狀態(tài)管理到有限狀態(tài)機(jī)的思考

新聞 前端
在我們前端開(kāi)發(fā)中,一定會(huì)接觸現(xiàn)在最熱門(mén)的幾大框架(Vue, React等等),在使用框架的過(guò)程中,我們一定會(huì)接觸某些狀態(tài)管理工具。

 1. 狀態(tài)管理

在我們前端開(kāi)發(fā)中,一定會(huì)接觸現(xiàn)在最熱門(mén)的幾大框架(Vue, React等等),在使用框架的過(guò)程中,我們一定會(huì)接觸某些狀態(tài)管理工具。

Vue我們會(huì)使用Vuex來(lái)管理全局狀態(tài), React會(huì)使用Redux來(lái)管理。

首先是不是,在問(wèn)為什么?

在使用類(lèi)似Vue,React框架時(shí),我們一定會(huì)使用狀態(tài)管理嗎?這個(gè)答案是肯定的。或許我不會(huì)主動(dòng)去使用Vuex, Redux,但我們編寫(xiě)每一個(gè)組件的時(shí)候就已經(jīng)在管理狀態(tài),Vuex, Redux只是更方便我們進(jìn)行全局的狀態(tài)管理。

為什么一定會(huì)使用狀態(tài)管理?這是因?yàn)楝F(xiàn)代前端框架使用數(shù)據(jù)驅(qū)動(dòng)視圖的形式來(lái)描述頁(yè)面。比如,Vue、 React組件會(huì)有一個(gè)自己內(nèi)部,外部的狀態(tài)來(lái)共同決定組件的如何顯示的,用戶(hù)與組件交互導(dǎo)致數(shù)據(jù)變更,進(jìn)而改變視圖。

框架 內(nèi)部狀態(tài) 外部狀態(tài)
Vue data props
React state, useState props

所以我們所寫(xiě)大部分業(yè)務(wù)邏輯,是在管理狀態(tài),框架會(huì)幫我們狀態(tài)映射成視圖,這可以說(shuō)是很經(jīng)典的MVVM模式。

  1. View = ViewModel(Model); 
  2. // 視圖 =  狀態(tài) + 管理 
  3. 復(fù)制代碼 

2. 有限狀態(tài)機(jī):計(jì)算機(jī)中一種用來(lái)進(jìn)行對(duì)象行為建模的工具

其作用主要是描述對(duì)象在它的生命周期內(nèi)所經(jīng)歷的狀態(tài)序列,以及如何響應(yīng)來(lái)自外界的各種事件。

我們來(lái)理解一下上面這段話。

  • 一種對(duì)象行為建模工具

    我們用來(lái)描述對(duì)象行為,狀態(tài)隨著時(shí)間轉(zhuǎn)變過(guò)渡行為的工具??梢阅M世界上大部分事物。

  • 生命周期

    我們通過(guò)抽象對(duì)象所經(jīng)歷的狀態(tài)序列,來(lái)確定對(duì)象一系列可能的生命周期和轉(zhuǎn)變。

  • 響應(yīng)外界事件

    外界事件能夠影響對(duì)象內(nèi)部狀態(tài)。對(duì)象能夠?qū)ν獠渴录鞒鲰憫?yīng)。

狀態(tài)機(jī)有基本幾個(gè)要素:

  1. 當(dāng)前所處狀態(tài)

    在各個(gè)時(shí)刻只處于一種狀態(tài)

  2. 狀態(tài)轉(zhuǎn)移函數(shù)

    在某種條件下,會(huì)從一種狀態(tài)轉(zhuǎn)移到另外一種狀態(tài)。

  3. 有限狀態(tài)序列

    擁有有限,可枚舉的狀態(tài)數(shù)

 

 


 

 上面這張圖所描述的狀態(tài)機(jī),我們使用js對(duì)象來(lái)進(jìn)行描述

  1. const stateTool = { 
  2.   // 當(dāng)前狀態(tài) 
  3.   currentState: '1'
  4.    
  5.   // 狀態(tài)轉(zhuǎn)變函數(shù) 
  6.   transition: (event) => { 
  7.   switch(event.type) { 
  8.       case '1': { 
  9.         this.currentState = event.status; 
  10.         doSomething1(); 
  11.         break
  12.       } 
  13.       case '2': { 
  14.         this.currentState = event.status; 
  15.         doSomething2(); 
  16.         break
  17.       } 
  18.       case '3': { 
  19.         this.currentState = event.status; 
  20.         doSomething3(); 
  21.         break
  22.       } 
  23.       default:  
  24.         console.log('Invalid State!'); 
  25.         break
  26.     } 
  27.   } 
  28. 復(fù)制代碼 

使用有限自動(dòng)機(jī)是一種狀態(tài)管理的思考方式,我們可以列舉組件狀態(tài)列表,設(shè)計(jì)觸發(fā)狀態(tài)函數(shù)。通過(guò)外部或內(nèi)部交互行為,觸發(fā)函數(shù)改變狀態(tài),根據(jù)狀態(tài)改變視圖

3. Flux思想

Flux是什么?Flux是一個(gè)Facebook開(kāi)發(fā)的、利用單向數(shù)據(jù)流實(shí)現(xiàn)的應(yīng)用架構(gòu)

簡(jiǎn)單說(shuō),F(xiàn)lux 是一種架構(gòu)思想,專(zhuān)門(mén)解決軟件的結(jié)構(gòu)問(wèn)題??梢哉f(shuō)他是有限狀態(tài)機(jī)的另外一種形式。

一個(gè)Flux管理分為4個(gè)狀態(tài):

  • View:視圖層

  • Action(動(dòng)作):視圖層觸發(fā)的動(dòng)作 or 行為

  • Dispatcher(派發(fā)器):收集觸發(fā)行為,進(jìn)行統(tǒng)一管理,統(tǒng)一分發(fā)給store層。

  • Store(數(shù)據(jù)層):用來(lái)存放應(yīng)用的狀態(tài),根據(jù)dispatcher觸發(fā)行為,就提醒Views要更新頁(yè)面

 

 


 

 

 

要是同學(xué)了解flux的的工作流程,那么很容易就發(fā)現(xiàn)這是一種工程化的狀態(tài)機(jī)。

 

  • 初始狀態(tài)

我們通過(guò)store 存放的是初始化狀態(tài),這種初始化狀態(tài)數(shù)據(jù)可以頁(yè)面初始化時(shí)設(shè)定 或 頁(yè)面加載時(shí)請(qǐng)求后端接口數(shù)據(jù),來(lái)初始化store數(shù)據(jù)。

通過(guò)store的初始化數(shù)據(jù),來(lái)構(gòu)建初始化的視圖層。

  • 狀態(tài)轉(zhuǎn)移事件

根據(jù)視圖層的行為會(huì)觸發(fā)action,我們通過(guò)統(tǒng)一的dispatcher來(lái)收集action, dispatcher將行為派發(fā)給store。

  • 狀態(tài)轉(zhuǎn)移函數(shù)

store通過(guò)判斷事件的類(lèi)型 和 payload,來(lái)修改內(nèi)部存儲(chǔ)狀態(tài)。達(dá)到狀態(tài)轉(zhuǎn)移的目的,并統(tǒng)一提醒view層更新頁(yè)面;

4. 全局到局部的狀態(tài)管理

既然我們是通過(guò)數(shù)據(jù)狀態(tài)來(lái)管理視圖的,那么在設(shè)計(jì)初期我們就可以從有限的狀態(tài)轉(zhuǎn)移來(lái)思考業(yè)務(wù)邏輯。通過(guò)思考每個(gè)狀態(tài)對(duì)應(yīng)的數(shù)據(jù),狀態(tài)轉(zhuǎn)移函數(shù),我們可以很清晰的羅列出數(shù)據(jù)更變邏輯。從數(shù)據(jù)去控制視圖也是現(xiàn)代前端所接觸到的MVVM模式。

一個(gè)大型應(yīng)用,我們也會(huì)使用Vuex 或 Redux來(lái)進(jìn)行一整個(gè)應(yīng)用的管理。

在平時(shí)的業(yè)務(wù)中,我們會(huì)遇到一個(gè)痛點(diǎn)是:Vuex,Redux是一個(gè)全局狀態(tài)管理,但我們現(xiàn)在需要在局部需要一個(gè)局部狀態(tài)管理變更,只能使用 mutation 或 dispatch 去提交更改。

如果我們頻繁的更新?tīng)顟B(tài),那么我們需要為每一個(gè)局部模塊編寫(xiě)大量dispatch函數(shù)來(lái)間接修改全局狀態(tài)。隨著應(yīng)用的擴(kuò)充,dispatch文件會(huì)越來(lái)越臃腫。

那么我們是不是可以使用不同的狀態(tài)管理工具,來(lái)實(shí)現(xiàn)局部狀態(tài)的管理。在局部狀態(tài)更新完之后,再去用局部更新去更新全局呢?

注:但這也會(huì)有一個(gè)缺點(diǎn),局部管理相對(duì)獨(dú)立。有些高度復(fù)用的提交函數(shù)需要放在全局狀態(tài)管理上

a. 框架原生組件狀態(tài)管理

React Hooks + React.createContext

React Hooks提供了useReducer + useContext + Context 可以實(shí)現(xiàn)一個(gè)小型的狀態(tài)管理

  1. // 以下代碼就實(shí)現(xiàn)了一個(gè)能夠穿透組件的狀態(tài)管理 
  2. import React, { useReducer, useContext } from 'react'
  3.  
  4. const reducer = (state = 0, { type, ...payload }) => { 
  5.   switch (type) { 
  6.     case 'add'
  7.       return state + 1
  8.     case 'desc'
  9.       return state - 1
  10.     default
  11.       return state; 
  12.   } 
  13.  
  14. const Context = React.createContext(); 
  15. const Parent = () => { 
  16.   const [state, dispatch] = useReducer(reducer, 0); 
  17.   return ( 
  18.     <> 
  19.       <Context.Provider value={{ state, dispatch }}> 
  20.         <Son /> 
  21.       </Context.Provider> 
  22.     </> 
  23.   ) 
  24.  
  25. function Son() { 
  26.   return <Counter /> 
  27.  
  28. function Counter() { 
  29.   const { state, dispatch } = useContext(Context); 
  30.   return ( 
  31.     <div> 
  32.       <button onClick={() => dispatch({ type: 'desc' })}>-</button> 
  33.       {state} 
  34.       <button onClick={() => dispatch({ type: 'add' })}>+</button> 
  35.     </div> 
  36.   ) 
  37. export default Parent; 
  38. 復(fù)制代碼 

Vue響應(yīng)式數(shù)據(jù) + vue.Provide/inject

使用vue響應(yīng)式系統(tǒng) + provide/inject API來(lái)實(shí)現(xiàn)一個(gè)具有穿透性的局部狀態(tài)管理

  1. // Parent.vue 
  2. <template> 
  3.   <Son /> 
  4. </template> 
  5.  
  6. <script setup> 
  7. import { provide, reactive, readonly } from "vue"
  8. import Son from "./Son.vue"
  9. const data = reactive({ 
  10.   count: 0
  11. }); 
  12. const onAdd = () => { 
  13.   data.count++; 
  14. }; 
  15. const onDesc = () => { 
  16.   data.count--; 
  17. }; 
  18. provide("store", { 
  19.   data: readonly(data), // 只讀屬性 
  20.   onAdd, // 修改函數(shù)add 
  21.   onDesc, // 修改函數(shù)desc 
  22. }); 
  23. </script> 
  24.  
  25. 復(fù)制代碼 
  1. // Son.vue 
  2. <template> 
  3.   <Counter /> 
  4. </template> 
  5.  
  6. <script setup> 
  7. import Counter from "./Counter.vue"
  8. </script> 
  9. 復(fù)制代碼 
  1. // Counter.vue 
  2. <template> 
  3.   <div> 
  4.     <button @click="store.onDesc">-</button> 
  5.     {{ store.data.count }} 
  6.     <button @click="store.onAdd">+</button> 
  7.   </div> 
  8. </template> 
  9.  
  10. <script setup> 
  11. import { inject } from "vue"
  12. const store = inject("store", {}); // 穿透讀取store 
  13. </script> 
  14. 復(fù)制代碼 

b. 線性狀態(tài)管理:Xstate

 

 

Xstate是一個(gè)很有趣的類(lèi)似有限狀態(tài)機(jī)的狀態(tài)管理, Xstate 著重點(diǎn)在于 管理狀態(tài) ,通過(guò) 狀態(tài)轉(zhuǎn)換去維護(hù)數(shù)據(jù) 。

我們來(lái)定義一個(gè)簡(jiǎn)單的promise狀態(tài)機(jī),使用官方提供的工具進(jìn)行可視化 

 

 

 

  1. import { Machine } from 'xstate'
  2.  
  3. // 創(chuàng)建狀態(tài)機(jī) 
  4. const promiseMachine = Machine({ 
  5.   id: 'promise'// 唯一id 
  6.   initial: 'pending'// 初始化狀態(tài) 
  7.   states: { // 狀態(tài)集合 
  8.     pending: { 
  9.       on: { 
  10.         RESOLVE: 'resolved'
  11.         REJECT: 'rejected'
  12.    } 
  13.     }, 
  14.     resolved: { 
  15.       type: 'final'
  16.     }, 
  17.     rejected: { 
  18.       type: 'final' 
  19.     } 
  20.   } 
  21. }) 
  22. 復(fù)制代碼 

注意:warning::狀態(tài)機(jī)不擁有狀態(tài),他只是定義狀態(tài)和定義狀態(tài)轉(zhuǎn)移

Xstate有提供函數(shù)來(lái)實(shí)現(xiàn)狀態(tài)機(jī)服務(wù),實(shí)現(xiàn)擁有狀態(tài)的實(shí)體

  1. import { interpret } from 'xstate' 
  2. const promiseService = interpret(promiseMachine).onTransition(state =>  
  3.   console.log(state.value) 
  4. // 創(chuàng)建服務(wù),指定狀態(tài)轉(zhuǎn)移時(shí)回調(diào)函數(shù) 
  5. promiseService.start() // 啟動(dòng)服務(wù) 
  6. promiseService.send('RESOLVE'); // 通知服務(wù)轉(zhuǎn)移狀態(tài),并執(zhí)行回調(diào)函數(shù) 
  7. 復(fù)制代碼 

這樣子我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Promise狀態(tài)機(jī)。他有很多應(yīng)用,可以結(jié)合Vue,結(jié)合React進(jìn)行使用。更加深入的內(nèi)容就需要到官方文檔中自行探索了!

就我個(gè)人的看法,狀態(tài)機(jī)思想非常適合狀態(tài)轉(zhuǎn)移相對(duì)線形的場(chǎng)景,在某些狀態(tài)多循環(huán)的場(chǎng)景轉(zhuǎn)移會(huì)相對(duì)復(fù)雜些

c. 可響應(yīng)式的狀態(tài)管理器:Mobx

mobx是一種響應(yīng)式的狀態(tài)管理,他所提倡的是拆分store做數(shù)據(jù)管理。這就很適合做局部的狀態(tài)管理,根據(jù)局部狀態(tài)管理來(lái)更新全局狀態(tài)。

相同的,我們舉個(gè)例子

  1. import { action, autorun, observable } from 'mobx' 
  2. import { observer } from 'mobx-react' 
  3. import React from 'react' 
  4.  
  5. const appStore = observable({ // 建立store 
  6.   count: 0
  7.   age: 18
  8. }) 
  9.  
  10. // autorun 只會(huì)觀察依賴(lài)的相關(guān)數(shù)據(jù) 
  11. // 使用當(dāng)appStore.age更新時(shí),才會(huì)觸發(fā)該函數(shù) 
  12. autorun(() => { 
  13.   // doSomething(); 
  14.   console.log('autorun', appStore.age); 
  15. }) 
  16.  
  17. const Counter = observer(() => { 
  18.   const { count } = appStore; 
  19.   const onAdd = action(() => { // 使用action更新store數(shù)據(jù) 
  20.     appStore.count++; 
  21.   }) 
  22.   const onDesc = action(() => { 
  23.     appStore.count--; 
  24.   }) 
  25.   return ( 
  26.     <div> 
  27.       <button onClick={onDesc}>-</button> 
  28.       {count} 
  29.       <button onClick={onAdd}>+</button> 
  30.     </div> 
  31.   ) 
  32. }) 
  33.  
  34. export default Counter; 
  35. 復(fù)制代碼 

5. 總結(jié)

現(xiàn)在前端主流使用數(shù)據(jù)驅(qū)動(dòng)視圖的形式,來(lái)實(shí)現(xiàn)業(yè)務(wù)。希望給大家?guī)?lái)兩點(diǎn)啟發(fā)

  1. 用有限狀態(tài)機(jī)去思考某些線性狀態(tài)場(chǎng)景的數(shù)據(jù)管理。

  2. 在之前的業(yè)務(wù)開(kāi)發(fā)的時(shí)候,就會(huì)出現(xiàn)一個(gè)痛點(diǎn),應(yīng)用全局狀態(tài)管理非常臃腫。

    在不斷功能迭代的過(guò)程中,需要做不同的狀態(tài)管理,雖然都是對(duì)同一份數(shù)據(jù)進(jìn)行維護(hù),但維護(hù)的方式不同,進(jìn)行一次狀態(tài)更新就需要編寫(xiě)一個(gè)不同的dispatch函數(shù)。隨著業(yè)務(wù)需求的增加,dispatch函數(shù)越來(lái)越多,難以管理和復(fù)用。

    思考如何解決這個(gè)問(wèn)題的時(shí),偶然看到了有限狀態(tài)機(jī)相關(guān)文章,思考到應(yīng)用的功能模塊在某一個(gè)時(shí)刻是相互獨(dú)立的,我們?cè)诰植繉?shù)據(jù)進(jìn)行更新,之后用一個(gè)全局函數(shù)對(duì)數(shù)據(jù)進(jìn)行統(tǒng)一替換。

注:本文為探索性質(zhì),使用原生組件進(jìn)行局部管理不需要引入依賴(lài)。但使用第三方工具造成包體積大小的增加,是否會(huì)增加性能消耗有待討論

 

責(zé)任編輯:張燕妮 來(lái)源: 高級(jí)前端進(jìn)階
相關(guān)推薦

2013-09-03 09:57:43

JavaScript有限狀態(tài)機(jī)

2014-05-21 11:09:56

前端有限狀態(tài)機(jī)

2021-09-07 06:40:26

狀態(tài)機(jī)識(shí)別地址

2022-03-06 19:57:50

狀態(tài)機(jī)easyfsm項(xiàng)目

2025-04-28 08:25:00

狀態(tài)機(jī)框架狀態(tài)機(jī)開(kāi)發(fā)

2023-03-06 07:35:30

狀態(tài)機(jī)工具訂單狀態(tài)

2023-04-12 07:14:31

Spring應(yīng)用業(yè)務(wù)

2010-06-18 12:38:38

UML狀態(tài)機(jī)視圖

2017-07-10 14:53:35

前端開(kāi)發(fā)MVVM模式有限狀態(tài)機(jī)

2020-03-27 10:50:29

DSL 狀態(tài)機(jī)工具

2010-06-18 13:25:44

UML狀態(tài)機(jī)視圖

2021-07-08 09:15:20

單片機(jī)編程狀態(tài)機(jī)編程語(yǔ)言

2022-05-28 16:08:04

前端

2010-07-08 13:03:31

UML狀態(tài)機(jī)圖

2011-06-24 16:09:24

Qt 動(dòng)畫(huà) 狀態(tài)機(jī)

2024-10-10 17:46:06

2011-08-22 10:52:30

iptables狀態(tài)

2025-04-14 09:30:11

Spring狀態(tài)機(jī)訂單

2021-08-19 09:00:00

微服務(wù)開(kāi)發(fā)架構(gòu)

2010-07-12 15:00:56

UML狀態(tài)機(jī)視圖
點(diǎn)贊
收藏

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