fre 1.5 發佈了……新的時間切片實現

你們好,真的很久不見………………由於如今暑假實習太忙,基本沒太有時間寫本身的東西了react

悲劇……因此一直想要重構 fre,拖到如今才實現,下面看看此次更新主要作了什麼事情:git

新的時間切片

在以前的版本中,時間切片主要是藉助 requestIdleCallback 完成的,它存在一些問題,好比兼容性不好(主要是小程序不支持),好比不可控(沒法控制切片的幀率,丟幀也不知道),總之這個 API 確定要搞掉的github

react 的時間切片,是基於 requestAnimation + 優先級 來調度實現的,理想狀態下,fre 也應該這麼作算法

const FPS = 1000 / 60

function workLoop (startTime = 0) {
  if (startTime && performance.now() - startTime > FPS) {
    requestAnimationFrame(workLoop)
  } else {
    const nextTime = performance.now()
    nextWork = performWork(nextWork)
    if (nextWork) {
      workLoop(nextTime)
    } else {
      options.commitWork
        ? options.commitWork(pendingCommit)
        : commitWork(pendingCommit)
    }
  }
}
複製代碼

如上圖,fre 一樣基於 requestAnimationFrame 實現了一個超級小的調度,它的原理是這樣的:小程序

  • 若是一個任務它可以在一幀內完成,那能夠同步的進入下一個任務
  • 若是有十個任務,他們均可以在一幀內完成,那麼同步的過程也不會卡(60fps)
  • 若是有任務沒法在一幀內完成,那麼它的下一個任務須要開啓下一幀
  • 下一幀的任務是下一個切片,由同步變成異步,保證複雜任務不干擾下一個任務

如上,短短的十行代碼,切片的同時,擁有較好的兼容性,並且切片的過程還可控,之後隨時能夠將更新隊列按照優先級進行排序緩存

diff 算法優化

如今的 fre 能夠說是孤立無援,react 的 diff 看不懂,其餘框架的 diff 不適用於 fiber框架

本次優化主要是針對兩個 case:異步

function App () {
  const [arr, setArr] = useState(['A', 'B','C','D'])
  return (
    <div> <ul> {arr.map(item => ( <A key={item} val={item}/> ))} </ul> <button onClick={() => setArr(['B','A','D','C'])}>+</button> </div> ) } function A(props){ return <div>{props.val}</div> } 複製代碼

如上,這個是其中一個 case ,將 key 加到組件上,此時的 key 應該轉移到組件內部的函數

根元素(div)上,這也是爲何咱們要求組件必須有一個元素包裹的緣由oop

若是不這麼作,key 加到組件上,等於沒加

{
  isShow && <A /> isShow ? <A /> : null isShow ? <A /> : <B /> } 複製代碼

這是另外一個 case,以前一直想優化這種狀況,發現很是困難,後來發現我是錯的

這種 case 是很難優化的,因此移除掉這部分的優化,尺寸也有史以來的第一次縮減到 1.7kb

位置的變化是很困難的,只有加 key 的狀況能夠作到位置變化

fix bug

終於搞懂了 useMemo / useCallback / useEffect 的關係,以前的實現有點點問題,新版本處理掉了

useMemo 是根據第二個參數判斷是否須要從新計算並返回一個緩存過的 值

useCallback是根據第二個參數判斷是否須要從新生成並返回一個緩存過的 函數引用

useEffect 是根據第二個參數判斷是否須要執行這個函數

總結 綜上所述,fre 新版本邏輯更加精巧了,我也按照約定,搞了時間切片和調度,至此,fre 算得上麻雀雖小,五臟俱全了

github 地址:github.com/132yse/fre

孤立無援,歡迎老鐵加入進來,一塊兒研究 fiber 下的算法~

相關文章
相關標籤/搜索