独自のライブラリを作成


ソース:https://pomb.us/build-your-own-react/
  • に入る前に.
  • x
  • 結果?JavaScriptで記述されたレスポンスコード
  • どうする?」『あなた自身の反応』を見てフォローし、知りたい部分は学習整理
  • 💡 目次
    **Step I**: The `createElement` Function
    **Step II**: The `render` Function
    **Step III**: Concurrent Mode
    **Step IV**: Fibers
    **Step V**: Render and Commit Phases
    **Step VI**: Reconciliation
    **Step VII**: Function Components
    **Step VIII**: Hooks

    1. createElement Function


    JSX —babel—> React.createElement
    reactの要素はreactです.createElement関数を使用して作成されますが、読みやすさが悪いため、JSX構文を使用します.JSX構文はbabelからJSに変換されます.React.createElement
    function createElement(type,props,...children){ // (type,props,[children])
        return {
            type,// tag name
            props:{
                ...props,
                children
            }
        }
    }
    // text를 처리하기 위한 함수
    function createTextElement(text) {
      return {
        type: 'TEXT_ELEMENT',
        props: {
          nodeValue: text,
          children: []
        }
      };
    }
    💡 突然のクイック演算子!
    ...子供は子供という配列要素のスプレッドシートです.したがって、type、props、および[children]になります.

    2. render Function

    ReactDOM.render
  • DOMノード
  • を作成
  • props
  • を追加
    生成ノード
  • をコンテナ
  • に追加する.
    function render(element,container){
    	const dom = 
    				element.type == "TEXT_ELEMENT"
    				? document.createTextNode("") // text node
    				: document.createElement(element.type) // element type으로 DOM노드 생성하기
    
      const isProperty = key => key !== 'children'
      // element에 props 추가
    	Object.keys(element.props)
    			.filter(isProperty)
    			.forEach(name => {
    				dom[name] = element.props[name]
    			})				
    	// children을 재귀적으로 
    	element.props.children.forEach(child=>
    		render(child,dom)	
    	)
    	container.appendChild(dom) // 생성한 node container에 추가
    }

    3.同時モード


    レンダー関数の再帰呼び出しセクションでは、ツリーの最後にレンダリングされるまで操作を停止しません.ツリーが大きくなると、メインスレッドは長い間停止します.ブラウザは、レンダリングを停止して優先度の高いタスクを処理する必要があります.
    function render(element, container) {
    ... 
     element.props.children.forEach((child) => render(child, dom)); 
    
      container.appendChild(dom);
    }
    したがって、タスクを小さなセルに分割し、各セルが完了した後にブラウザにレンダリングを停止させます.
    let nextUnitOfWork = null
    
    function workLoop(deadline) {
      let shouldYield = false
      while (nextUnitOfWork && !shouldYield) {
        nextUnitOfWork = performUnitOfWork(
          nextUnitOfWork
        )
        shouldYield = deadline.timeRemaining() < 1
      }
      requestIdleCallback(workLoop)
    }
    
    requestIdleCallback(workLoop)
    
    function performUnitOfWork(nextUnitOfWork) {
      // TODO
    }
    requestIdleCallback:requestIdleCallback loopを作成するために使用します.ブラウザのプライマリ・スレッドがアイドル状態の場合、requestIdleCallbackが呼び出されます.

    4. Fibers



    各作業単位を構造化するためには、fiber treeの資料構造が必要である.
    上記の操作を行うと、render関数の内部にルートファイバが作成され、nextUnitOfWorkに設定されます.残りの作業はperformUnitOfWork機能で発生します.
    繊維ごとに3つのことをします.
  • 要素をDOMの
  • に追加
  • 元素を製造する子供が添加する繊維
  • 次の勤務先を選択します.
  • fiber - children - ling - parent
    繊維樹の目的は、次の作業単位を見つけやすいことです.
    1本の繊維が仕事を終えると、子供がいれば、兄弟姉妹は次の職場になります.
    兄弟姉妹がいない場合は、ルートに戻るまで親に表示されます.

    5. Render and Commit


    現在のコードでは、新しいノードがDOMに追加されると、ブラウザはレンダリングツリーが完了する前に操作を停止できます.この場合、未完了のUIが表示されます.
    このため,DOMの論理を削除するのではなく,ファイバルーティングを追跡する.これをwipRoot(work in progress rot)と呼びます.
    すべての作業が完了すると(次の作業がない場合)、繊維ツリー全体がドームにコミットされます.
    この処理はcommitRoot関数で行います.ここでは、domにすべてのノードを再帰的に追加します.

    6.回復(再調整)


    Nodeのupdateとdeleteはrender関数を用いて得られたelementを最後にコミットされたfiberツリーと比較する.
    このため、提出後に最後の繊維樹を保存します.これを現在のルートと呼びます.
    その後、すべての繊維特性にalternateを追加します.これは以前の段階で提出された繊維です.
    次いで、performUnitOfWorkから新しい繊維を生成する論理を抽出し、reconcileChildren関数を生成する.従来の繊維の子供と再調整が必要な元素を比較するために、同時に巡回する.
    ElementとoldFiberのtypeの比較
  • タイプが同じならアイテムのみ交換-update
  • タイプは異なり、新しい要素であり、新しいDOMノードを生成します.placement
  • タイプは異なり、古いファイバがある場合は古いノードを削除-
  • を削除effectTagプロパティを追加します.
    削除するノードを追跡する代理配列を作成します.
    commitRootでdelegation配列を巡回してcommitWorkを実行します.commitWork関数では、effectTag追加、removeChildまたはupdateを使用します.
    更新のためにupdateDom関数を作成します.
    従来の繊維の支柱と新繊維の支柱とを比較し,消失した支柱を取り除き,異なる支柱を設けた.onで始まるprops(event)がある場合はeventListenerを追加します.

    7.関数構成部品


    関数型素子から作製したファイバはDOMノードを持たない.だから子供たちを道具に持ち込むのではなく、関数を呼び出します.
    繊維タイプが関数であるかどうかを確認した後、updateFunctionComponentに変更してこれまでと同じ役割を果たした.

    8. Hook


    関数構成部品にステータスを追加します.
    const Myact = {
      createElement,
      render,
      useState,
    }
    
    /** @jsx Didact.createElement */
    function Counter() {
      const [state, setState] = Didact.useState(1)
      return (
        <h1 onClick={() => setState(c => c + 1)}>
        Count: {state}
    	</h1>
    	)
    }
    const element = <Counter />
    const container = document.getElementById("root")
    Didact.render(element, container)
    let wipFiber = null
    let hookIndex = null
    
    function updateFunctionComponent(fiber) {
      wipFiber = fiber
      hookIndex = 0
      wipFiber.hooks = []
      const children = [fiber.type(fiber.props)]
      reconcileChildren(fiber, children)
    }
    関数型構成部品を呼び出す前に  useState  関数の内部で使用するためにグローバル変数を初期化する必要があります.
    まず作業中の繊維を設定します.さらに,このファイバはhook 배열を追加し,同じ素子がusState関数を複数回呼び出すことを可能にした.
    function useState(initial) {
      const oldHook =
            wipFiber.alternate &&
            wipFiber.alternate.hooks &&
            wipFiber.alternate.hooks[hookIndex]
      const hook = {
        state: oldHook ? oldHook.state : initial,
      }
      
      wipFiber.hooks.push(hook)
      hookIndex++
      return [hook.state]
    }
    関数型構成部品がuserStateを呼び出すと、古いhookであるかどうかを確認し、hookインデックスを使用してファイバのalternateを確認します.
    古いhookを持っている場合、初期化ステータスがない場合は、このhookのステータスを新しいhookにコピーします.
    その後、新しいHookをファイバに追加し、Hookインデックス値を増やしてstateに戻ります.
    const hook = {
      state: oldHook ? oldHook.state : initial,
      queue: [],
    }
      
        const setState = action => {
          hook.queue.push(action)
          wipRoot = {
            dom: currentRoot.dom,
            props: currentRoot.props,
            alternate: currentRoot,
          }
          nextUnitOfWork = wipRoot
          deletions = []
        }
          
    wipFiber.hooks.push(hook)
    hookIndex++
    return [hook.state, setState]
    }
    また、userStateは、更新状態の関数も返さなければならないため、動作を受信するsetState関数も定義します.このアクションをHookに追加するヒントに追加します
    次に、レンダー関数と同様の操作を行い、新しいwipルートを次の単位に設定して、繰り返し文で新しいレンダーステップを開始します.
    const actions = oldHook ? oldHook.queue : []
    actions.forEach(action => {
      hook.state = action(hook.state)
    })
    まだアクションが実行されていません.これは、素子レンダリング後に実行され、古いHookのキューからすべてのアクションが取得され、新しいHook stateに適用され、更新されたstateが取得されます.

    新知

  • Reactの要素はReactです.createElement関数を使用して作成されますが、読みやすさが悪いため、JSX構文を使用します.JSX構文はbabelからJSに変換されます.
  • 反応は、繊維樹構造を用いて作業単位を構造化する.
  • virtual DOMは、render関数によって得られる要素、すなわちJavaScriptオブジェクトである.反応器は前の木と新しい木を同時に巡回して比較した.