tiny-react助你看懂react源碼

tiny-react一個基於React17精簡而來的倉庫

tiny-react是一個爲了簡化react源碼學習的庫,和react17的區別就是少了不少功能,只實現了核心的邏輯,和preact這種react-like庫有着根本區別,preact更像是一個和react有着相同的接口可是實現細節卻不盡相同的react,而tiny-react是從react官方倉庫精簡而來,它更像官方react的閹割版,因此每一行代碼,每個函數都能在react最新的官方倉庫中找到出處,並且總共的代碼只有6千多行,刨除掉ReactDOM只有4000多行,能讓React源碼學習的難度大大下降react

使用指南

  • 在閱讀源碼前,你須要對react的大致原理有必定的瞭解在這裏強烈推薦去通讀一下卡頌老師的React技術揭祕
  • 對react的大致原理有必定了解後就能夠開始看源碼了,不過有些同窗可能對一些源碼中使用頻繁的的數據結構和算法還不怎麼了解,這時候就能夠看一下下面的看源碼前須要瞭解的數據結構和算法,若是你已經很是瞭解這些知識則能夠跳過
  • react-dom這個模塊,能夠不用太關注,雖然他的代碼有2000多行,可是大量的代碼都是dom操做和事件委託相關函數,對學習React核心原理影響不大,不過其中 瀏覽器事件優先級 相關的代碼仍是要注意一下
  • react-reconciler這個模塊須要重點關注特別是其中的 ReactFiberWorkLoop 他是React源碼的骨幹,把全部的模塊鏈接到了一塊兒
  • scheduler這個模塊是代碼最少,最簡單的模塊了,並且基本沒有和其餘模塊耦合,能夠直接單獨看他的源碼

看源碼前須要瞭解的數據結構和算法

Feature

時間切片

/**
 *  打開performance查看Concurrent Mode下render階段的時間切片
 * 點擊一下按鈕而後會開始BitList的render階段,能夠看處處於render階段(也就是點擊按鈕後的前幾秒)時input
 * 是能夠輸入的,可是因爲要渲染的東西太多,到commit階段時就會開始卡住,
 * 因此此時會卡頓的瓶頸在瀏覽器渲染太耗時,而不是在react
 */

import React, { useState } from '../packages/react'

const data = Array.from({ length: 50e4 }, (_, i) => i)
const CHUNK_SIZE = 1e4 / 10

/**
 * fiber是最小的工做粒度,若是要保證render過程當中能保證瀏覽器能
 * 處於交互的狀態就得保證一個fiber render的過程不會太耗
 * 事件,因此能夠根據機能設置合適的CHUNK_SIZE
 * @param param0
 * @returns
 */
const Chunk = ({ start }: { start: number }): any => {
  const end = Math.min(data.length, start + CHUNK_SIZE)
  const children = Array.from({ length: end - start })
  for (let i = start; i < end; ++i) {
    children[i - start] = <div key={i}>{i}</div>
  }

  return children
}

const BigList = () => {
  const children = []

  for (let i = 0; i < data.length; i += CHUNK_SIZE) {
    children.push(<Chunk start={i} />)
  }
  return <div>{children}</div>
}

export const TimeSlicingDemo = () => {
  const [isShowBigList, setIsShowBigList] = useState(false)

  return (
    <div>
      <button
        onClick={() => {
          //因爲點擊事件內產生的更新會按Sync優先級處理
          //咱們手動用setTimeout去掉點擊事件的執行上下文
          setTimeout(() => {
            setIsShowBigList(!isShowBigList)
          })
        }}
      >
        toggle BigList
      </button>
      <br />
      <input placeholder="輸入點東西,看看交互有沒有被阻塞" />
      <br />
      {isShowBigList ? <BigList /> : null}
    </div>
  )
}

image

useState和useEffect

image

useLayoutEffect

image

優先級調度

import React, { useState, useEffect } from '../packages/react'

export const PriorityScheduling = () => {
  const [count, updateCount] = useState(0)

  const onClick = () => {
    updateCount((count) => count + 2)
  }

  console.log({ count })

  useEffect(() => {
    //暫時不支持ref直接用選擇器獲取
    const button = document.getElementById('discretEventDispatcher')!
    setTimeout(() => updateCount(1), 1000)
    setTimeout(() => {
      button.click()
      //根據機能給第二個setTimeout一個合適的時間,或者適當的加長數組的長度
      //以保證點擊事件觸發時,前一個低優先級的更新的render階段尚未完成
      //才能看到插隊狀況發生
    }, 1030)
  }, [])

  return (
    <div>
      <button id="discretEventDispatcher" onClick={onClick}>
        增長2
      </button>
      <div>
        {Array.from(new Array(10000)).map((v, index) => (
          <span key={index}>{count}</span>
        ))}
      </div>
    </div>
  )
}

image

相關文章
相關標籤/搜索