fre代码阅读

hook.ts

import { update, isFn, getCurrentFiber } from "./reconcile"
import {
  DependencyList,
  Reducer,
  IFiber,
  Dispatch,
  SetStateAction,
  EffectCallback,
  HookTypes,
  RefObject,
  IEffect,
  FreNode,
} from "./type"

const EMPTY_ARR = []

// cursor 是当前 hook 的个数
let cursor = 0

export const resetCursor = () => {
  cursor = 0
}

// 传入:initState
// 返回:一个有两个元素的数组 hook
// hook[0]: state
// hook[1]: dispatch
export const useState = <T>(initState: T): [T, Dispatch<SetStateAction<T>>] => {
  return useReducer(null, initState)
}


// 传入:
// reducer:一个函数,描述 state 如何变化,以及如何响应 actions 并发送到 store
// initState
// 返回值:一个有两个元素的数组 hook
// hook[0]: initState
// hook[1]: 修改当前 state 的函数,在 useState 中是 setXX,在 useReducer 中是 dispatch
export const useReducer = <S, A>(
  reducer?: Reducer<S, A>, // 即 (prevState: STATE, action: ACTION) => STATE 类型,传入之前的 state,和要如何对这个 state 进行修改的 action,返回新 state 的
  initState?: S
): [S, Dispatch<A>] => {

  // 新增一个 hook
  const [hook, current]: [any, IFiber] = getHook<S>(cursor++)

  // 初始化这个 hook
  if (hook.length === 0) {
    hook[0] = initState

    // hook[1] 为修改 hook[0](当前 state)的函数
    hook[1] = (value: A | Dispatch<A>) => {
      // 如果 reducer 不为空,如 useReducer
      //     调用 reducer(initState, value),将 hook[0] 修改为新的值
      // 如果 reducer 为空,例如 useState
      //     看传入的描述修改的 value 是什么类型
      //     value 是 Function,如 setCount((count)=>count+1),则调用里面的 Function((count)=>count+1)
      //         注意:原生 React 不支持这种写法
      //     value 是一个值,如 setCount(3),则直接给 count 赋值(3)
      hook[0] = reducer
        ? reducer(hook[0], value as any)
        : isFn(value)
          ? value(hook[0])
          : value

      update(current)
    }
  }
  return hook
}

// 传入:
// cb: dependency 变化时要执行的函数
// deps:dependency list
// 返回:null
export const useEffect = (cb: EffectCallback, deps?: DependencyList): void => {
  return effectImpl(cb, deps!, "effect")
}


// 类似于 useEffect,但 useLayout 是同步的,阻塞 UI
// 传入:
// cb: dependency 变化时要执行的函数
// deps:dependency list
// 返回:null
export const useLayout = (cb: EffectCallback, deps?: DependencyList): void => {
  return effectImpl(cb, deps!, "layout")
}

// 传入:
// cb: dependency 变化时要执行的函数
// deps:dependency list
// key: hook 的类型,useEffect 为 "effect",useLayout 为 "layout"
const effectImpl = (
  cb: EffectCallback,
  deps: DependencyList,
  key: HookTypes
): void => {
  const [hook, current] = getHook(cursor++)
  if (isChanged(hook[1], deps)) {
    hook[0] = cb
    hook[1] = deps
    current.hooks[key].push(hook)
  }
}

// 如果 deps 变化了,返回执行 cb 后的新的 state,否则返回旧的 state,state 不变
// 传入:
// cb: dependency 变化时要执行的函数,为 ()=>STATE 类型
// deps:dependency list
// 返回值:state
export const useMemo = <S = Function>(
  cb: () => S,
  deps?: DependencyList
): S => {
  const hook = getHook<S>(cursor++)[0]
  if (isChanged(hook[1], deps!)) {
    hook[1] = deps
    return (hook[0] = cb())
  }
  return hook[0]
}

// 对 useMemo 进行封装,useCallback(cb, deps) 等价于 useMemo(()=>cb, deps)
// 传入:
// cb: dependency 变化时要执行的函数
// deps:dependency list
// 返回值:state
export const useCallback = <T extends (...args: any[]) => void>(
  cb: T,
  deps?: DependencyList
): T => {
  return useMemo(() => cb, deps)
}

// 对 useMemo 进行封装,useRef(x) 等价于 useMemo(()=>{x}, [])
// 首次 t=useRef(null),其实什么都没做
// 
t
后,t 这个 ref 才被绑定到 Element,此时 t 的值为 { current:
t
}
export const useRef = <T>(current: T): RefObject<T> => { return useMemo(() => ({ current }), []) } // 传入:cursor // 返回:一个 hook 数组 // hook[0]:[state, dependency] // hook[1]:current export const getHook = <S = Function | undefined, Dependency = any>( cursor: number ): [[S, Dependency], IFiber] => { const current: IFiber<any> = getCurrentFiber() // 如果 current.hooks 为 null,则将其初始化为 { list: [], effect: [], layout: [] } // useState 和 use const hooks = current.hooks || (current.hooks = { list: [], effect: [], layout: [] }) // 如果要新增一个 hook,在 hooks.list 里插入一个新的 hook if (cursor >= hooks.list.length) { hooks.list.push([] as IEffect) } // list[0] 是 state // list[1] 是 dependency return [(hooks.list[cursor] as unknown) as [S, Dependency], current] } // { // whatever: { // value: T; // children: FreNode // }; // initialValue: Text; // } export type ContextType<T> = { ({ value, children }: { value: T, children: FreNode }): FreNode; initialValue: T; } type SubscriberCb = () => void; // 从组件中读取和订阅上下文 // export const createContext = <T>(initialValue: T): ContextType<T> => { const contextComponent: ContextType<T> = ({ value, children }) => { const valueRef = useRef(value) const subscribers = useMemo(() => new Set<SubscriberCb>(), EMPTY_ARR) if (valueRef.current !== value) { valueRef.current = value; subscribers.forEach((subscriber) => subscriber()) } return children } contextComponent.initialValue = initialValue; return contextComponent; } export const useContext = <T>(contextType: ContextType<T>): T => { let subscribersSet: Set<Function> const triggerUpdate = useReducer(null, null)[1] as SubscriberCb useEffect(() => { return () => subscribersSet && subscribersSet.delete(triggerUpdate) }, EMPTY_ARR); let contextFiber = getCurrentFiber().parent while (contextFiber && contextFiber.type !== contextType) { contextFiber = contextFiber.parent } if (contextFiber) { const hooks = contextFiber.hooks.list as unknown as [[RefObject<T>], [Set<SubscriberCb>]] const [[value], [subscribers]] = hooks; subscribersSet = subscribers.add(triggerUpdate) return value.current } else { return contextType.initialValue } } export const isChanged = (a: DependencyList, b: DependencyList) => { return !a || a.length !== b.length || b.some((arg, index) => !Object.is(arg, a[index])) }

type.ts

// Fiber 存储了 dom 相关信息、fiber 树相关的引用、要更新时的 side effect 等
// 通过 Fiber 可以实现跟踪、调度、暂停和中止工作,形成异步,解决 react 组件树比较庞大时,更新会阻塞 js 主线程的问题
export interface IFiber<P extends Attributes = any> {
  key?: string
  type: string | FC<P>
  parentNode: HTMLElementEx
  childNodes: any

  node: HTMLElementEx // 真实 dom 节点

  kids?: any

  parent?: IFiber<P> // 父 fiber
  sibling?: IFiber<P> // 下一个兄弟 fiber
  child?: IFiber<P> // 第一个子 fiber

  done?: () => void
  ref: IRef
  hooks: IHook
  oldProps: P
  after: any
  props: P

  lane: number // 优先级

  time: number
  next: IFiber
  dirty: boolean
  isComp: boolean
}

你可能感兴趣的:(javascript,开发语言,ecmascript)