首頁>Club>
8
回覆列表
  • 1 # 使用者5198606283124

    React的Suspense功能,簡單說就是讓元件渲染遇到需要非同步操作的時候,可以無縫地“懸停”(suspense)一下,等到這個非同步操作有結果的時候,再無縫地繼續下去。

    這裡所說的非同步操作,可以分為兩類:

    非同步載入程式碼非同步載入資料

    很好辨認,寫程式嘛,折騰的無外乎就是“程式碼”和“資料”這兩樣東西。

    為什麼要非同步載入程式碼呢?

    本來,程式碼打包成一個檔案就行,但是當代碼量很龐大,而且也不是所有程式碼都是頁面載入的時候就用得上,把他們拉進唯一的打包檔案,除了打包過程簡單沒有任何好處。所以,為了榨取效能,就要考慮把程式碼打包成若干檔案,這樣每個打包檔案就可以比較小,根據需要來載入。這是一個好主意,不過讓每個開發人員都實現這套機制也是夠扯的,所以,React就用Suspense提供了統一的無縫的程式碼分割(Code Splitting)兼非同步載入方法,在v16.6.0就實現了這樣的Suspense功能。

    大家有興趣自己去玩,這種Suspense不是今天要講的重點,今天要講的是“非同步載入資料”的Suspense,也就是利用Suspense來呼叫伺服器API之類的操作。

    根據React官方的路線圖,利用Suspense來做資料載入,要等到今年(2019)的中期才釋出,你如果看到這篇文章比較晚,可能已經發布了。

    今天要說的,是Suspense做資料載入,和React v16的重頭戲非同步渲染有一點矛盾的地方。

    在之前的Live 《深入理解React v16新功能》中我說過,從React v16開始,一個元件的生命週期可以分為兩個階段:render階段+commit階段。在render階段的生命週期函式,因為Fiber的設計特點,可能會被打斷,被打斷之後,會重新被呼叫;而commit階段一旦開始,就絕不會被打斷。render階段和commit階段的分界線是render函式,注意,render函式本身屬於render階段。

    舉個例子,一個元件被渲染,執行到函數里面,這時候使用者突然在某個input控制元件裡輸入了什麼,這時候React決定去優先處理input控制元件裡的按鍵事件,就會打斷這個元件的渲染過程,也就是不管返回啥,渲染過程都就此打住,不畫了,專心去處理input控制元件的事情去了。等到那邊的事情處理完,再來渲染這個元件,但是這時候從原來位置重新開始,那肯定是不靠譜的,因為剛才的按鍵事件處理可能改變了一些狀態,為了保證絕對靠譜,React決定……還是從頭走一遍吧, 於是,重新去調、然後呼叫。

    看到沒喲,render之前的生命週期函式都會被呼叫,而且,因為這種“打斷”是完全是不可預期的,所以,現在就要求在render階段的所有生命週期函式不要做有副作用的操作。

    什麼叫副作用?就是純函式不該做的操作。

    什麼叫純函式?就是除了根據輸入引數返回結果之外,不做任何多與事情的操作。

    如果一個函式修改全域性變數,那就不是一個純函式;如果一個函式修改類例項狀態,那就不是一個純函式;如果一個函式丟擲異常,那就不是一個純函式;如果一個函式透過AJAX訪問伺服器API,那就不是一個純函式。

    就拿訪問伺服器API為例,假如render階段的生命週期函式做了訪問伺服器API的AJAX操作,那麼,很有可能產生連續對伺服器的訪問,因為非同步渲染下render階段會被打斷而重複執行啊。

    再說一遍,在render階段的所有生命週期函式不要做有副作用的操作,這些函式必須是純函式。

    那麼,現在問題來了,使用Suspense來獲取資料,會不會違反者這個規定呢?

    雖然Suspense這方面的API還沒有確定,但是程式碼形式還是明確的,利用試玩版的react-cache展示一下。

    這裡就有意思了,使用Suspense來獲取資料,既然資料是在render函式(或者像上面例子一樣在函式型別元件中)使用,那麼獲取資料的過程肯定是在render階段,但是,獲取資料的過程是要呼叫AJAX的啊,AJAX是副作用操作,這不就和“render階段不能做有副作用操作“的規定矛盾了嗎?

    的確有點矛盾。

    Reac開發人員對這個的解釋是這樣:

    簡單說來,就是降低了要求,只需要render階段的操作是”冪等“(indempotent)就可以了。

    所謂冪等,就是一次呼叫和N次呼叫產生一樣的結果。

    還是舉例來說吧。

    上面的foo3這個函式,的確有副作用,但是,利用程式碼巧妙地防一手,只讓第一次呼叫發出AJAX,之後的呼叫就不發AJAX了,這樣,呼叫多少次,產生的效果都一樣,這就是”冪等“。

    冪等雖然沒有純函式那麼純,但是也足夠好了,至少對於React這樣無法做到”純“的框架,這也是最好的結果。

    試玩版react-cache的函式,接受一個返回Promise的函式為引數,獲取的Promise是會被cache住的,所以,雖然會被打斷重複多次,如果有返回結果,那麼返回的結果都是一樣 ,也就達到了”冪等“的效果。

    這麼一看,矛盾也就解決了。

    總結一下,實際上我們要把”在render階段的所有生命週期函式不要做有副作用的操作,這些函式必須是純函式“這個要求改一下,改成”在render階段的所有生命週期函式都應該冪等“。

    雖然一說React往往都說要說“函數語言程式設計”,但是React真的先天和崇尚純函式的“函數語言程式設計”有很大距離,包括Hooks,表面上看推崇函式式元件,似乎是向函數語言程式設計邁進了一步,但是,所有的Hooks函式都是有狀態的,怎麼能算純函式呢。

    當然,有潔癖一樣追求純函式也沒有必要,既然“冪等”能夠解決問題,我們也樂見其成。

    印證了那句老話:只要你降低標準,會發現世界豁然開朗:)

    P.S. 這篇文章裡的背景知識如果不清楚,就去看我之前的Live吧,該說的知識點我都說過了。

    《快速瞭解React的新功能Suspense和Hooks》

    《深入理解React v16新功能》

    《幫助你深入理解 React》

  • 中秋節和大豐收的關聯?
  • 現在的年輕人學什麼專業好?