React學習第九天---React & Fiber(建立任務隊列和完成任務調度邏輯"requestIdelCallback「)(二)

這是我參與更文挑戰的第21天,活動詳情查看: 更文挑戰react

你們好我是小村兒,在上節Fiber算法開發環境的基本配置,你們也能夠看源碼跟着學習,還介紹了一下核心API 'requestIdelCallback',介紹了Fiber核心思路,咱們接下來實現Fiber算法,一探究竟。git

項目代碼, 你們能夠持續跟進學習,共勉!!!github

Fiber算法準備工做: 建立任務隊列並添加任務

1. 準備一段JSX代碼

先準備一段JSX代碼,咱們下面就看看如何利用Fiber算法如何一步一步將JSX代碼轉換成真實DOM對象,而後顯示在顯示在頁面當中。算法

import React from "./react"

const jsx = (<div> <p>Hello React</p> </div>)

console.log(jsx)

複製代碼

2. 建立createElement方法將JSX轉化爲VirtualDOM對象

JSX解析須要一個React.createElement方法,咱們建立一個react文件,建立一個react文件夾,默認導出一個類,這個類有一個createElement方法,這個方法很簡單就是咱們以前建立過的createElement方法複製過來便可瀏覽器

// react/index.js
import createElement from './createElement';

export default {
  createElement
}

// CreateElement/indexjs

export default function createElement(type, props, ...children) {
  const childElements = [].concat(...children).reduce((result, child) => {
    if (child !== false && child !== true && child !== null) {
      if (child instanceof Object) {
        result.push(child)
      } else {
        result.push(createElement("text", { textContent: child }))
      }
    }
    return result
  }, [])
  return {
    type,
    props: Object.assign({ children: childElements }, props)
  }
}
複製代碼

暫時簡化一些,就不須要返回children。 目錄結構:babel

image.png

查看效果: image.pngmarkdown

成功得到VirtualDOM對象!!!dom

3. 建立一個生成任務隊列函數將VirtualDOM對象轉化到任務隊列中

接下來咱們要把這個VirtualDOM渲染到頁面當中,則須要一個render方法,這個render方法也須要從 react 文件夾中的index.js中導出,因此咱們先建立一個文件夾reconciliation,在這裏面的index.js文件下咱們建立render方法, Fiber算法會將VirtualDOM轉化爲一個個小任務。因此在在render中咱們須要在svn

  1. 向任務隊列中添加任務
  2. 指定在瀏覽器空閒時執行任務

以後這些任務就是經過vdom對象構建fiber對象函數

因此咱們在react文件夾下建立一個Misc文件夾,表明是雜項的意思,什麼意思呢?就是當咱們處理主業務的時候,確定有一些輔助的函數,這些輔助的函數就能夠放入這個雜項的文件下進行管理 ,咱們再在Misc文件夾下建立一個CreateTaskQueue文件夾,這個文件夾用來建立任務隊列,CreateTaskQueue函數返回一個對象,返回一個對象有兩個屬性,push,pop完成隊列的先進先出操做

// CreateTaskQueue/index.js
const createTaskQueue = () => {
  const taskQueue = [];
  return {
    /** * * 向任務隊列中添加任務 */
    push: item => taskQueue.push(item),
    /** * 從任務隊列中獲取任務 */
    pop: () => taskQueue.shift()
  }
}
export default createTaskQueue




// reconciliation/index.js
import { createTaskQueue } from '../Misc'

const taskQueue = createTaskQueue();

export const render = (element, dom) => {
  /** * 1. 向任務中添加任務 * 2. 指定在瀏覽器空閒時執行任務 * * */
  /** * 任務就是經過 vdom 對象 構建 fiber 對象 * * */
 taskQueue.push({
   dom,
   props: {children: element}
 })

 console.log(taskQueue.pop())
}

// react/index.js
import createElement from './createElement';
export {render} from "./reconciliation" // 導出render方法
複製代碼

這樣咱們就能夠將vdom轉化成任務隊列了

image.png

4. 小結

咱們先準備一段JSX代碼,babel會將這JSX一段代碼轉化成React.createElement方法調用,因此咱們在次實現一下createElement方法(看了我以前tinyReact的)直接複製過來便可,這樣就能夠將 JSX 轉化爲VirtualDOM對象。咱們在react文件夾下導出一個對象,將createElement放入該對象中,完成React.createElement,咱們在建立一個文件夾reconciliation在這個文件夾下放下Fiber算法的核心邏輯,在這個文件夾下的index裏面有一個render方法並導出,在react/index.js再引用並導出,因此在src/index.js才能按需導出render方法,render方法須要兩個參數element, domdom是父級,element是子級。接下來咱們要從元素的最頂層依次去向下查找查找到每個元素的VirtualDOM,咱們要爲每一個VirtualDOM建立fiber對象,因此咱們建立一個任務隊列,每添加的一個任務就是一個對象,包含了父級,包含了子級。dom就是最外層的父級。咱們還建立了一個Misc文件夾,這個文件夾叫作雜項,在實現Fiber算法的時候呀,確定會有一些輔助方法,這些輔助方法都會放在Misc中。咱們在這個雜項文件家中建立第一個輔助方法,createTaskQueue,這個方法就是用來建立任務隊列的,爲何要建立任務隊列呢?由於咱們要執行的任務不止一個,咱們在執行任務以前,都要把這些任務放入這個任務隊列當中,createTaskQueue返回一個對象,對象有兩個屬性,pushpoppush:向任務隊列中添加任務, pop:從任務隊列中獲取任務, 這樣達到方便Fiber算法內部調動。

實現任務的調度邏輯

上面咱們完成了Fiber算法的基本目錄結構,和建立了Fiber算法須要的任務隊列,而且完成了添加任務準備工做,接下來咱們來實現任務調度邏輯

1. 在render方法內調用requestIdelCallback

咱們在render方法中調用Fiber算法核心API requestIdelCallback,完成指定在瀏覽器空閒時執行任務,咱們將執行任務函數名取爲performTask,意思就是執行任務的意思

export const render = (element, dom) => {
 /** * 指定在瀏覽器空閒的時間去執行任務 */
 requestIdleCallback(performTask)
}
複製代碼

2. 實現performTask方法

咱們以前說過Fiber算法會將一個大任務拆分紅一個一個小任務,一個個小任務就須要採用循環的方式來調用,因此performTask作的第一件事是循環調用一個個小任務,咱們將這件事處理函數命名爲workLoop。將deadline傳遞進去。

const performTask = deadline => {
  // 將一個大任務拆解成一個個小任務而且循環處理
  workLoop(deadline)
}

複製代碼

3. 實現workLoop方法

workLoop 是用來循環處理一個個小任務的,而且接收deadline這個參數。

  1. 當任務不存在

這個方法第一件事情就是判斷當前要執行任務存不存在,(咱們在頂部聲明一個常量 subTask(子任務),默認值爲null,這個任務呢在taskQueue中獲取)。若是任務不存在則去taskQueue裏面去獲取並賦值給subTask,獲取方法名咱們取爲getFirstTask(獲取任務隊列第一個任務),咱們暫時把這個方法置空便可。

const getFirstTask = () => {}

const workLoop = deadline => {
  if(!subTask) {
    subTask = getFirstTask()
  }
}
複製代碼
  1. 當任務存在

若是任務存在而且瀏覽器有空餘時間咱們則須要執行這個任務,並且這個subTask任務不止一個,因此咱們採用循環的方法去執行這個任務,循環裏面執行任務的操做,咱們再封裝一個函數executeTask表明執行任務的意思,executeTask執行完之後必須返回一個新的任務回來,只有返回一個新的任務這個while循環才能繼續去執行,executeTask而且接收一個參數,實際上寫個參數就是已是fiber對象了,今天咱們只實現任務的調度邏輯,fiber對象的實現咱們暫且留一個坑。

const workLoop = deadline => {
  if(!subTask) {
    subTask = getFirstTask()
  }
  // 若是任務存在且瀏覽器存在空閒時間就去執行這個任務

  while(subTask && deadline.timeRemaining() > 1) {
    subTask = executeTask()
  }
}
複製代碼
  1. 當有更高級的任務被執行的狀況

咱們須要考慮的一種狀況, 任務在執行的過程當中,瀏覽器這時候有一個更高優先級的任務要執行那麼瀏覽器沒有空餘時間,這個任務執行就會被打斷,那麼workLoop函數執行完退出,這時候的performTask就執行到最後就結束了。

可是咱們有可能任務還沒處理完,若是等到高級任務被執行完成咱們必須從新去註冊這個任務。

也就是說咱們在performTask最後面,不但還要去判斷下subTask是否有值,(有值,就說明任務尚未執行完)。並且還要判斷taskQueue裏面是否有任務,(若是taskQueue裏面還有任務的話咱們仍是要在瀏覽器空閒的時候去執行任務).

這裏咱們就須要在taskQueue裏面實現一個方法isEmpty判斷是否存在任務. 因此當subTask還有值或者taskQueue.isEmptyfalse咱們還須要調用requestIdleCallback(performTask)

// react/Misc/CreateTaskQueue/index.js
const createTaskQueue = () => {
  const taskQueue = [];
  return {
    ···
    /** * 判斷任務隊列中是否還有任務 */
    isEmpty: () => taskQueue.length === 0
  }
}
export default createTaskQueue

// 調度任務
const performTask = deadline => {
  workLoop(deadline)
  if(subTask || !taskQueue.isEmpty) {
    requestIdleCallback(performTask)
  }
}

複製代碼

4. 實現executeTask

上面也說了executeTask這個方法是執行任務,而且返回一個新的任務,且須要的一個參數就是Fiber對象

const executeTask = fiber => {
    return newSubTask
}
複製代碼

暫時executeTask先實現僞代碼,第十天咱們在揭曉

總結

今天咱們主要是作了三件事:

  1. 建立基本目錄

以一段JSX代碼做爲出發點,構建了一個實現Fiber算法的目錄結構,建立一個React文件夾,下面有三個文件夾和一個index.js文件,reconciliation是存放Fiber算法核心代碼目錄,Misc(雜項)存放在處理Fiber核心算法輔助代碼目錄,CreateElement這裏存放createElemnt方法。

  1. 完成建立任務隊列並添加任務

在Misc目錄中實現一個建立任務隊列方法createTaskQueue返回一個任務隊列,而後在reconciliation的render方法中添加任務

3.實現任務的調度邏輯

使用requestIdleCallbackAPI和循環實現任務的調度邏輯,在requestIdleCallback調用performTask方法,這裏面在循環調用任務workLoop,workLoop考慮有任務和沒任務的狀況,沒任務去taskQueue獲取任務,有的話且瀏覽器有空閒時間則執行任務,執行任務完以後讓循環繼續則須要返回新任務,在這個任務執行的過程還須要考慮瀏覽器是否須要處理高級任務,當高級任務處理完成,還須要查看任務隊列是否還有任務subTask是否還有任務有則從新使用requestIdleCallbackAPI從新循環上面操做。

好了今天就完成這三件事,咱們能夠不只學習Fiber算法思想,還能夠學習目錄結構的思想,還能夠學習到代碼組織技巧,還有命名很重要哈哈哈,後面咱們會學習Fiber對象的構建,敬請期待!!!若是你能堅持看到這裏,但願點贊,或者評論說出你的疑惑點,共同窗習,謝謝!!!

相關文章
相關標籤/搜索