国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前端狀態管理請三思

魏憲會 / 859人閱讀

摘要:它們是單向數據流和狀態容器,而不是狀態管理。幾個月之前我開始尋找可以解決狀態管理問題的模式,最終我發現了狀態機的概念。狀態機不接受沒有明確定義的輸入作為當前的狀態。狀態機強制開發者以聲明式的方式思考。

最近我開始思考React應用的狀態管理。我已經取得一些有趣的結論,并且在這篇文章里我會向你展示我們所謂的狀態管理并不是真的在管理狀態。

譯者:阿里云前端-也樹

原文鏈接:managing-state-in-javascript-with-state-machines-stent

我們避而不談的是什么(The elephant in the room)

我們來看一個簡單的例子。想象這是一個展示用戶名稱、密碼和一個按鈕的表單組件。用戶會在填寫表單后點擊提交。如果一切順利,我們完成了登錄,并且有必要展示歡迎信息和一些鏈接:


我們假定這個組件有兩個展示狀態。一個是未登錄狀態,另一個是用戶登錄后的狀態。所以從管理這兩種狀態開始,我們用一個布爾值的標志位來描述用戶的狀態。

var isLoggedIn;
isLoggedIn = false; // 展示表單
isLoggedIn = true; // 展示歡迎信息和鏈接

但是這樣還不夠。如果我們點擊提交按鈕后觸發的HTTP請求需要一些時間來響應,我們不能把表單孤零零的放在屏幕上,而需要更多的UI元素來展示這樣的中間狀態,因此我們不得不在組件中引入另一個狀態。

現在我們有了第三種展示狀態,僅僅用一個 isLoggedIn 變量已經不能解決了。不走運的是我們不能設置變量值為 false-ish,它不是 true 也不是 false。當然,我們可以引入另一個變量比如說 isInProgress。一旦我們發送請求就會把這個變量的值置為 true。這個變量會告訴我們是處于請求的過程中并且用戶應該看到加載中的展示狀態。

var isLoggedIn;
var isInProgress; 

// 展示表單
isLoggedIn = false;
isInProgress = false;

// 請求過程中
isLoggedIn = false;
isInProgress = true;

// 展示歡迎信息和鏈接
isLoggedIn = true;
isInProgress = false;

非常棒!我們用到兩個變量并且需要記住這三種情況對應的變量值。看起來我們解決了問題。但另外的問題是,我們維護了太多狀態。如果我們需要展示一個請求成功的信息,或者一切順利的時候我們需要告知用戶:“Yep, 你成功登錄了”,并且兩秒后信息伴隨著華麗的動畫隱藏起來,接著展示出最終的界面,要怎么辦?


現在情況變得有些復雜。我們有了 isLoggedInisInProgress,但是看起來僅僅使用它們還不夠。isInProgress 在請求結束后確實是 false,但是他的默認值同樣是 false。我覺得我們需要第三個變量 - isSuccessful

var isLoggedIn, isInProgress, isSuccessful;

// 展示表單
isLoggedIn = false;
isInProgress = false;
isSuccessful = false;

// 請求過程中
isLoggedIn = false;
isInProgress = true;
isSuccessful = false;

// 展示成功狀態
isLoggedIn = true;
isInProgress = false;
isSuccessful = true;

// 展示歡迎信息和鏈接
isLoggedIn = true;
isInProgress = false;
isSuccessful = false;

我們簡單的狀態管理一步步變成了由 if-else 組成的巨大的條件網,很難去理解和維護。

if (isInProgress) {
  // 請求過程中
} else if (isLoggedIn) {
  if (isSuccessful) {
    // 展示請求成功信息
  } else {
    // 展示歡迎信息和鏈接
  }
} else {
  // 等待輸入,展示表單
}

我們還有一個問題會讓這個情景變得更糟:如果請求失敗我們要怎么做?我們需要展示一個錯誤信息和一個重試鏈接,如果點擊重試我們會重復一次請求的過程。

現在我們的代碼已經沒有任何可維護性。我們有非常多的場景需要滿足,僅僅依賴引入新的變量是不可接受的。讓我們想想是否可以通過更好的命名方式來解決,同時可能還需要引入一個新的條件聲明。

isInProgress 僅僅在請求的過程中被用到。我們現在還關心請求結束之后的過程。

isLoggedIn 有一點誤導的含義,因為我們只要請求結束就把它置為 true。而如果請求出錯,用戶并沒有真正登入。所以我們把它重命名為 isRequestFinished。雖然看起來好些了,但是它僅僅代表我們從服務器獲得了響應,并不能用它來判斷響應是否為錯誤。

isSuccessful 是一個最終狀態合適的候選變量。如果請求出錯我們可以把它設置為 false,但是等等,它的默認值也是 false。所以它也不能作為代表錯誤狀態的變量。

我們需要第四個變量,isFailed 怎么樣?

var isRequestFinished, isInProgress, isSuccessful, isFailed;

if (isInProgress) {
  // 請求過程中
} else if (isRequestFinished) {
  if (isSuccessful) {
    // 展示請求成功信息
  } else if (isFailed) {
    // 展示請求失敗信息和重試鏈接
  } else {
    // 展示歡迎信息和鏈接
  }
} else {
  // 等待輸入,展示表單
}

這四個變量描述了一個看似簡單但實際并不簡單的過程,這個過程包含了許多邊界情況。當項目進一步迭代時,最終可能會由于已有變量的組合不能滿足新的需求,而定義更多的變量。這就是構建用戶界面十分困難的原因。

我們需要更好的狀態管理方式。也許可以使用更現代和更流行的概念。

Flux 或者 Redux 怎么樣?

最近我在思考 Flux 架構和 Redux 庫在狀態管理中的定位。即使這些工具和狀態管理有關,但是它們本質上不是解決這類問題的。

Flux 是 Facebook 用來構建客戶端 web 應用的架構。它利用單向數據流補足了 React 的視圖組件的組織方式。

Redux 是一個可預測的狀態容器,用來構建 JavaScript 應用。

它們是 “單向數據流” 和 “狀態容器”,而不是 “狀態管理”。Flux 和 Redux 背后的概念是非常實用和討巧的。我認為它們是適合構建用戶界面的方式。單向數據流讓數據擁有可預測性,改進了前端開發。Redux 中的 reducer 擁有的不可變特性,提供了一種可以減少 bug 的數據傳送方式。
就我的感受來說,這些模式更適用于數據管理和數據流管理。它們提供了完善的 API 來交換改變我們應用數據的信息,但是并不能解決我們狀態管理的問題。這也因為這些問題是跟項目強相關的,問題的上下文取決于我們正在做的事情。
當然像處理 HTTP 請求我們可以通過某個庫來解決,但是對其它相關的業務邏輯我們仍然需要自己編寫代碼來實現。問題在于我們如何用一種合適的方式去組織這些代碼,而不至于每兩年就把整個應用重寫一遍。

幾個月之前我開始尋找可以解決狀態管理問題的模式,最終我發現了狀態機的概念。事實上我們一直都在構建狀態機,只不過我們不知道。

什么是狀態機?

狀態機的數學定義是一個計算模型,我的理解是:狀態機就是保存你的狀態和狀態變化的一個盒子。這里有一些不同種類的狀態機,適用于我們這個案例的是有限狀態機。像它的名字一樣,有限狀態機包含有限的幾種狀態。它接收一個輸入并且基于這個輸入和當前的狀態決定下一個狀態,可能會有多種情況輸出。當狀態機改變了狀態,我們就稱為它過渡到一個新的狀態。

實戰狀態機

為了使用狀態機我們或多或少需要定義兩件事 - 狀態和可能的過渡方法。讓我們來嘗試實現上面提到的表單需求。

在這個表格中我們可以清楚的看到所有狀態和他們可能的輸出情況。我們同樣定義了如果輸入被傳遞進狀態機后的下一個狀態。編寫這樣的表格對你的開發周期大有裨益,因為他會回答你以下問題:

用戶界面可能出現的所有狀態有哪些?

每種狀態之間會發生什么?

如果某種狀態改變,結果是什么?

這三個問題可以解決非常多的難題。想象一下當我們改變內容展示的時候有一個動畫效果,當動畫開始時,UI 仍然處于之前的狀態并且用戶仍然可以產生交互。舉個例子,用戶非常快速地點擊了兩次提交按鈕。如果不適用狀態機,我們需要使用if語句通過標志變量來防止代碼的執行。但是如果回到上面那個表格,我們會看到 loading 狀態不接受 Submit 狀態的輸入。所以如果我們在第一次點擊按鈕后把狀態機轉變為 loading 狀態,我們就會處于一個安全的位置。即使 Submit 輸入/動作被分發過來,狀態機也會忽略它,當然也不會再向后端發出一個請求。

狀態機模式對我來說是適用的。以下有三個理由支撐我在我的應用中使用狀態機:

狀態機模式免去了很多可能出現的 bug 和奇怪的清潔,因為它不會讓 UI 變化為我們不知道的狀態。

狀態機不接受沒有明確定義的輸入作為當前的狀態。這會免去我們對其它代碼執行的部分容錯處理。

狀態機強制開發者以聲明式的方式思考。因為我們大部分的邏輯需要提前定義。

在 JavaScript 里實現狀態機

現在,既然我們知道什么是狀態機,那就讓我們來實現一個并且解決我們一開始的問題。用一些嵌套的屬性定義一個簡單的對象字面量。

const machine = {
  currentState: "login form",
  states: {
    "login form": {
      submit: "loading"
    },
    "loading": {
      success: "profile",
      failure: "error"
    },
    "profile": {
      viewProfile: "profile",
      logout: "login form"
    },
    "error": {
      tryAgain: "loading"
    }
  }
}

這個狀態機對象使用我們上面表格中的內容定義了狀態。像示例中那樣,當我們在 login form 狀態時,我們用 submit 作為一個輸入并且應該以 loading 狀態結束。現在我們需要一個接收輸入的函數。

const input = function (name) {
  const state = machine.currentState;

  if (machine.states[state][name]) {
    machine.currentState = machine.states[state][name];
  }
  console.log(`${ state } + ${ name } --> ${ machine.currentState }`);
}

我們獲得了當前狀態并且檢查提供的input是否合法,如果通過檢查,我們就改變當前的狀態,或者換句話說,將狀態機過渡到一個新的狀態。我們提供了一個日志輸出用來輸入、當前狀態和新的狀態(如果有變化的話)。下面是如何去使用我們的狀態機:

input("tryAgain");
// login form + tryAgain --> login form

input("submit");
// login form + submit --> loading

input("submit");
// loading + submit --> loading

input("failure");
// loading + failure --> error

input("submit");
// error + submit --> error

input("tryAgain");
// error + tryAgain --> loading

input("success");
// loading + success --> profile

input("viewProfile");
// profile + viewProfile --> profile

input("logout");
// profile + logout --> login form

注意我們嘗試通過在 login form 狀態的時候發送 tryAgain 狀態來打破狀態機的運轉或者是重復發送提交請求。在這些場景下,當前的狀態沒有被改變并且狀態機會忽略這些輸入。

最后的話

我不知道狀態機的概念是否適用于你自己的場景,但是對我來說非常適用。我僅僅改變了我處理狀態管理的方式。我建議去嘗試一下,絕對是值得的。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89524.html

相關文章

  • 阿里云前端周刊 - 第 31 期

    摘要:發布按照官方發布計劃,的發布意味著進入階段,徹底退出舞臺,的還有半年結束。為了應對這個挑戰,美團點評境外度假前端研發團隊自年月起啟動了面向端用戶的赫爾墨斯項目。前端技術越來越復雜,有不低的技術門檻。 推薦 1. 利用 Dawn 工程化工具實踐 MobX 數據流管理方案 https://zhuanlan.zhihu.com/p/... 項目在最初應用 MobX 時,對較為復雜的多人協作項...

    madthumb 評論0 收藏0
  • 前端模塊化——技術選型

    摘要:前言前端模塊化,主要是解決兩個問題命名空間沖突,文件依賴管理。目前解決的方法是模塊化命名空間各個模塊的命名空間獨立。模塊化構建工具,等是用來組織前端模塊的構建工具加載器。 前言 前端模塊化,主要是解決兩個問題——命名空間沖突,文件依賴管理。 坑___命名空間沖突 我自己測試好的代碼和大家合并后怎么起沖突了? 頁面腳本的變量或函數覆蓋了公有腳本的。 坑___文件依賴管理 明明項目需...

    Doyle 評論0 收藏0
  • Web UI自動化最佳實踐

    摘要:三思而后行自動化測試最終目的是啥投入產出比的最佳平衡點在哪很多實施者在搭建自動化框架前往往缺乏思考,為了自動化而自動化。 三思而后行 UI自動化測試最終目的是啥?投入產出比的最佳平衡點在哪?很多實施者在搭建UI自動化框架前往往缺乏思考,為了自動化而自動化。三思而后行,方向決定成敗。由于項目接口(API and Service)自動化代碼行覆蓋率已經達到70%,基于當前自動化人力和項目質...

    canger 評論0 收藏0
  • 機械轉行前端,半年零基礎自學的心路歷程

    摘要:受上海杰克大大委托,于今晚分享一下本人的自學歷程主題機械轉行前端,半年零基礎自學的心路歷程。所以我就這半年個人自學修行以來的一些感觸和心得方面進行分享。背景介紹內容前工作狀況機械離職經歷心態轉變目標確定大家好,我是,一枚前端萌新。 機械轉行前端,半年零基礎自學的心路歷程 標簽: 轉行 自學 原創:Michael.Lu 277133779@qq .com 轉載注明出處 這是初級群(西安...

    baihe 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<