你們在寫App和一些單頁面程序的時候,常常會遇到這樣的狀況:
當點擊左邊的箭頭的時候,會出現灰黑色的抽屜效果,再點擊一下向左的箭頭,就會收起來,固然向右滑動和向左滑動也能實現抽屜效果的開關。還有,當抽屜效果開着的時候,點擊右側區域也會自動收起抽屜。點擊左側抽屜裏面的圖標,那麼也會發生抽屜效果的開關。html
若是咱們用原生js去手動控制抽屜的開關效果,隨着邏輯的複雜,會存在兩個嚴重的問題:react
1.若是隻是向左箭頭點擊一次開,再點擊一次關成對兒出現還好,可是若是出現好比點擊一下左側按鈕或者點擊右側內容區域關閉就容易出現點擊關閉不掉或者不應關閉的狀況下關閉了,抽屜狀態的控制會隨着業務邏輯的複雜度增長變得十分不可控。程序員
2.若是要想增長一個邏輯控制抽屜開關,那麼就得從一堆的if else 詳細代碼裏,找到最後的else 部分增長else if,一來是修改起來很是困難,而來若是趕上條件組合判斷和嵌套,對於開發者簡直是一個噩夢,若是出現一個bug,你得把全部的邏輯捋順一遍,這對於一個複雜的項目是很是耗時,幾乎是不可能的。es6
因爲app愈來愈複雜,視圖隨着數據和邏輯的手動更新狀態變得愈來愈困難。在這種狀況下,Angualr、React、Vue等框架應運而生。尤爲是React,在狀態管理無出其右。那麼問題來了,爲何React就在狀態管理方面遊刃有餘呢?chrome
由於它使用了狀態模式。這事兒其實很是的簡單,咱們看看怎麼回事兒。設計模式
狀態模式(狀態機)app
什麼是狀態模式或狀態機?框架
一個對象在其內部狀態改變時改變它的行爲,這個對象看起來就像改變了它的類同樣。
當你們看到定義的時候,是否是有一種單個字都認識合起來徹底看不懂的感受?這就比如直播吃翔四個字,單看這四個字感受沒啥,合起來就感受怎麼這麼噁心。ide
不過不要緊,咱們來看看狀態模式究竟是個啥玩意。你們千萬別聽見設計模式和狀態機之類的詞語本身先蒙圈嚇到了,我直接寫一個狀態機,你一看就明白。
你們家裏都用過冰箱吧,點擊電源按鈕.若是是洗衣機是開着的on狀態, 當你點擊它會發出off的信號,若是你是off狀態,則會發出on的信號.代碼實現以下:函數
var switches = (function(){ var state = "off"; return function(){ if(state === "off"){ console.log("打開洗衣機"); state = "on"; }else if(state === "on"){ console.log("關閉洗衣機"); state = "off"; } } })(); //按下開關按鈕 oBtn.onclick = function(){ switches(); };
上面的代碼徹底符合咱們學過的js的東西,簡單的一個匿名函數自執行,加一個變量,ok這就是一個狀態機,是否是so easy。
可是做爲一個有追求的洗衣機,得可以針對不一樣的洗衣機選擇的不一樣的程序,沒錯洗衣機面板上也是程序這兩個字,發明洗衣機的必定是程序員。
程序裏面有不少的洗滌方式,好比智能、標準、快速、強力,輕柔等等。咱們豐富一下咱們的面板。
var switches = (function(){ var state = "智能"; return function(){ if(state === "智能"){ console.log("智能洗衣模式"); state = "標準"; }else if(state === "標準"){ console.log("標準洗衣模式"); state = "快速"; }else if(state === "快速"){ console.log("快速洗衣模式"); state = "強力"; }else if(state === "強力"){ console.log("強力洗衣模式"); state = "輕柔"; }else if(state === "輕柔"){ console.log("輕柔洗衣模式"); state = "智能"; } } })(); //按下程序按鈕 oBtn.onclick = function(){ switches(); };
看起來沒問題,可是你懂的,首先寫10中洗衣模式if寫法會被寫死,switch方式也很很差。
我有一個大膽的想法,能不能不用判斷語句?你們看我這麼思考能不能搞定這件事?
其實洗衣機面板其實就是一個狀態對應一個行爲,
一個對象在其內部狀態改變時改變它的行爲,這個對象看起來就像改變了它的類同樣。
這句話就好理解了,你們想洗衣機若是原本是浸泡狀態,可是我把它改爲脫水狀態,洗衣機從最開始的浸泡衣服變成甩乾衣服,看起來就好像洗衣機從一個浸泡機器變成了甩幹機器同樣。
換成這種思路理解,咱們就能夠不用判斷了,只要記住狀態改變,行爲改變就好了。咱們試着實現一下洗衣模式。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script> window.onload = function(){ //定義狀態 var Zhineng = function(button){ this.turn = button; } Zhineng.prototype.press= function(){ console.log('智能模式'); this.turn.setState("Biaozhun"); } var Biaozhun = function(button){ this.turn = button; } Biaozhun.prototype.press= function(){ console.log('標準模式'); this.turn.setState("Kuaisu"); } var Kuaisu = function(button){ this.turn = button; } Kuaisu.prototype.press= function(){ console.log('快速模式'); this.turn.setState("Qiangli"); } var Qiangli = function(button){ this.turn = button; } Qiangli.prototype.press= function(){ console.log('強力模式'); this.turn.setState("Qingrou"); } var Qingrou = function(button){ this.turn = button; } Qingrou.prototype.press= function(){ console.log('輕柔模式'); this.turn.setState("Zhineng"); } //定義洗衣機洗衣程序 var Washer = function(){ this.Zhineng = new Zhineng(this); this.Biaozhun = new Biaozhun(this); this.Kuaisu = new Kuaisu(this); this.Qiangli = new Qiangli(this); this.Qingrou = new Qingrou(this); this.state = "Zhineng"; } Washer.prototype.setState = function(state){ this.state=state; } Washer.prototype.press = function(){ this[this.state].press(); //執行對應狀態的press } Washer.prototype.init = function(){ //定義執行者 document.querySelector('#btn').addEventListener("click",()=>{ this.press(); },false); } var w = new Washer(); w.init(); //初始化 w.state = "Qiangli"; }; </script> </head> <body> <input type="button" value="按下" id="btn"> </body> </html>
這樣就拜託了if判斷,咱們想要什麼模式,只須要經過改變對應的狀態就行了。可是不少人會想說:
大量重複代碼太難受了啊,針對這樣的狀況,es6中推出了狀態機這個僞函數,可以幫助咱們快速實現狀態化。
也就是傳說中的generator函數。 目前FF,edge,chrome 最新版本已經支持。
generator
咱們用generator優化一下咱們上面的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script> window.onload = function () { //這裏一堆變量,其實咱們能夠用一個對象,可是咱們不搞那麼複雜 var Zhineng = function () { console.log("智能模式"); } var Biaozhun = function () { console.log("標準模式"); } var Kuaisu = function () { console.log("快速模式"); } var Qiangli = function () { console.log("強力模式"); } var Qingrou = function () { console.log("輕柔模式"); } function* models() { for (var i = 0, fn, len = arguments.length; fn = arguments[i++];) { yield fn(); if (i === len) { i = 0; } } } var exe = models(Zhineng,Biaozhun,Kuaisu,Qiangli, Qingrou); //按照模式順序排放 document.querySelector("#btn").addEventListener("click", function () { exe.next(); }, false); }; </script> </head> <body> <input type="button" value="按下" id="btn"> </body> </html>
這裏我解釋下,Generator 函數是一個普通函數,可是有三個特徵:
1.function關鍵字與函數名之間有一個星號;
2.函數體內部使用yield表達式,定義不一樣的內部狀態
3.遍歷器對象的next方法,使得指針移向下一個狀態
簡單的說generator就是讓函數排隊一個一個去執行了,很簡單對不對。
上面咱們說過react的狀態管理無出其右,那咱們看看它的狀態管理跟咱們寫的有什麼異同之處呢?
react的狀態管理
咱們平時開發組件時不少時候要切換組件的狀態,每種狀態有不一樣的處理方式,這個時候就可使用狀態模式進行開發。
用和上面同樣的思路,咱們來舉一個React組件的小例子,好比一個Banner導航,最基本的兩種狀態,顯示和隱藏,以下:
咱們先把generator思路翻篇,咱們回到狀態模式,先看一段代碼
const States = { "show": function () { console.log("banner展示狀態,點擊關閉"); this.setState({ currentState: "hide" }) }, "hide": function () { console.log("banner關閉狀態,點擊展示"); this.setState({ currentState: "show" }) } };
你們如今來看,這個不就是「react版本的洗衣機」嘛。只不過咱們定義的是單個的狀態變量,而這裏經過一個對象States來定義banner的狀態,這裏有兩種狀態show和hide,分別擁有相應的處理方法,調用後再分別把當前banner改寫爲另一種狀態。
接下來來看導航類Banner,這裏吧狀態要給拿過來作對照了,這樣看着醒目:
const States = { "show": function () { console.log("banner展示狀態,點擊關閉"); this.setState({ currentState: "hide" }) }, "hide": function () { console.log("banner關閉狀態,點擊展示"); this.setState({ currentState: "show" }) } }; //上面代碼不應出如今這只是爲了說明問題 class Banner extends Component { constructor(props) { super(props); this.state = { currentState: "hide" } this.toggle = this.toggle.bind(this); } toggle() { const s = this.state.currentState; States[s] && States[s].apply(this); } render() { const { currentState } = this.state; return ( <div className="banner" onClick={this.toggle}> </div> ); } }; export default Banner;
經過導航組件你們這麼想,其實react就是有不少種洗衣狀態可是必定能數出來多少種洗衣模式的洗衣機,咱們每一次的操做都是在按洗衣機的按鈕。上面的代碼只不過是再開關洗衣機而已。
你不用糾結爲何這麼作就可以進行狀態管理。
你用洗衣機強力模式的時候,你也沒考慮過洗衣機桶和馬達怎麼運做的吧?你只要知道按下哪一個按鈕能洗乾淨衣服,大致上知道洗衣機裏面是經過馬達帶動衣服和滾筒之間摩擦清除污漬就夠了。
同理,你只要知道改變狀態可以實現要的功能,大致上的原理就是狀態機就能夠了。
有了這個基礎,若是想繼續深刻就能夠去看react源碼以及進行各類開發了。
總結,本文重點:
1.狀態機模式的使用場景,複雜多狀態的管理,這裏注意你不必寫個選項卡之類的用狀態機,那反而是給本身找麻煩。技術源於生活和解決問題,你洗一雙襪子也不會扔洗衣機裏面吧?
2.generaor的使用,這個你們要學會和靈活運用,我只是淺顯的講了基礎的應用,這個對你的開發和代碼簡化會大有用途。
3.react的狀態管理,你們要知道原理,重在應用,等用多了你再去看源碼,就可以更加深入的體會做者爲何那麼寫,原理和熟練應用一個都不能少,可是要注意前後順序和特定學習階段該瞭解到的程度。