3
Watch
179
Star
18
Fork
0
Issue

基于react17精简而来的tiny-react

PiNengShaoNian
PiNengShaoNian
pushedAt 4 months ago

PiNengShaoNian/tiny-react

tiny-react是一个基于React17精简而来的仓库

为了简化react源码学习的库,和react17的区别就是少了很多功能,只实现了核心的逻辑,和preact这种react-like库有着根本区别,preact更像是一个和react有着相同的接口但是实现细节却不尽相同的react,而tiny-react是从react官方仓库精简而来,它更像官方react的阉割版,所以每一行代码,每一个函数都能在react最新的官方仓库中找到出处,而且总共的代码只有6千多行,刨除掉ReactDOM只有4000多行,能让React源码学习的难度大大降低

使用指南

  • 在阅读源码前,你需要对react的大体原理有一定的了解在这里强烈推荐去通读一下卡颂老师的React技术揭秘
  • 对react的大体原理有一定了解后就可以开始看源码了,不过有些同学可能对一些源码中使用频繁的的数据结构和算法还不怎么了解,这时候就可以看一下下面的看源码前需要了解的数据结构和算法,如果你已经非常了解这些知识则可以跳过
  • react-dom这个模块,可以不用太关注,虽然他的代码有2000多行,但是大量的代码都是dom操作和事件委托相关函数,对学习React核心原理影响不大,不过其中 浏览器事件优先级 相关的代码还是要注意一下
  • react-reconciler这个模块需要重点关注特别是其中的 ReactFiberWorkLoop 他是React源码的骨干,把所有的模块连接到了一起
  • scheduler这个模块是代码最少,最简单的模块了,而且基本没有和其他模块耦合,可以直接单独看他的源码

看源码前需要了解的数据结构和算法

Feature

时间切片

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

memo

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

type Color = '#fff' | 'green'
type Component =
  | 'NestedComponent'
  | 'NormalComponent'
  | 'MemorizedComponentWithUnstableProps'
  | 'MemorizedNestedComponent'
  | 'MemorizedComponentWithCustomCompareFunc'
  | 'MemorizedComponent'

const prevColors: Record<Component, Color> = {
  NestedComponent: 'green',
  NormalComponent: 'green',
  MemorizedComponentWithUnstableProps: 'green',
  MemorizedNestedComponent: 'green',
  MemorizedComponentWithCustomCompareFunc: 'green',
  MemorizedComponent: 'green',
}

const useCurrentOutlineStyle = (componentName: Component): CSSProperties => {
  const currColor = prevColors[componentName] === '#fff' ? 'green' : '#fff'
  prevColors[componentName] = currColor
  return {
    outline: `1px solid ${currColor}`,
  }
}

const NormalComponent = () => {
  return (
    <div>
      NormalComponent
      <NestedComponent />
      <MemorizedNestedComponent />
    </div>
  )
}

const NestedComponent = () => {
  const outlineStyle = useCurrentOutlineStyle('NestedComponent')
  return <div style={outlineStyle}>-- NestedComponent</div>
}

const MemorizedNestedComponent = memo(() => {
  const outlineStyle = useCurrentOutlineStyle('MemorizedNestedComponent')

  return <div style={outlineStyle}>-- MemorizedNestedComponent</div>
})

const MemorizedComponent = memo(() => {
  const outlineStyle = useCurrentOutlineStyle('MemorizedComponent')

  return <div style={outlineStyle}>MemorizedComponent</div>
})

const MemorizedComponentWithUnstableProps = memo<{ count: number }>(
  ({ count }) => {
    const outlineStyle = useCurrentOutlineStyle(
      'MemorizedComponentWithUnstableProps'
    )

    return (
      <div style={outlineStyle}>
        MemorizedComponentWithUnstableProps {count}
      </div>
    )
  }
)

const MemorizedComponentWithCustomCompareFunc = memo<{ text: string }>(
  ({ text }) => {
    const outlineStyle = useCurrentOutlineStyle(
      'MemorizedComponentWithCustomCompareFunc'
    )

    return <div style={outlineStyle}>最大字符长度-8 {text}</div>
  },
  (oldProps, newProps) => newProps.text.length > 8
)

export const MemorizedComponentDemo = () => {
  const [count, setCount] = useState(0)
  const [text, setText] = useState('')
  return (
    <div>
      <MemorizedComponent />
      <br />
      <NormalComponent />
      <br />
      <MemorizedComponentWithUnstableProps count={count} />
      <br />
      <MemorizedComponentWithCustomCompareFunc text={text} />
      <br />
      <button
        onClick={() => {
          setCount(count + 1)
        }}
      >
        incrment-{count}
      </button>
      <br />
      <input
        placeholder="输入"
        onChange={(e) => {
          setText(e.target.value)
        }}
      />
    </div>
  )
}

image

ucloud ads