본문으로 건너뛰기

React Fiber 완전 이해

무료2018-01-06#Front-End#JS#React Fiber介绍#React Fiber工作原理#React Fiber源码剖析#React Fiber guide#Fiber reconciler

알고 있음 - 구함 - 풀이 - 유추

일.목표

Fiber 는 React 코어 알고리즘의 리팩토링이며, 2 년간의 리팩토링 산물이 Fiber reconciler 입니다

핵심 목표:그적용성을 확대하는 것. 애니메이션, 레이아웃, 제스처를 포함. 5 개의 구체적인 목표로 나뉨 (뒤 2 개는 덤):

  • 중단 가능한 작업을 작은 태스크로 분할

  • 진행 중인 작업의 우선순위 조정, 재시도, 이전 (절반까지 끝난) 성과 재사용

  • 부모 자식 태스크 간에从容히 전환 (yield back and forth), React 실행 프로세스 중 레이아웃 업데이트 지원

  • render() 가 여러 요소를 반환하는 것 지원

  • error boundary 를 더 잘 지원

본래의 취지는 JS 가 제어 불가능하게 장시간 실행되는 것을 원하지 않는 것 (수동으로 스케줄링하고 싶음) 인데, 왜 JS 의 장시간 실행이 인터랙션 응답, 애니메이션에 영향을 미치는가?

JavaScript 는 브라우저의 메인 스레드에서 실행되며, 마침내 스타일 계산, 레이아웃, 많은 경우 드로잉과 함께 실행된다. JavaScript 실행 시간이 너무 길면, 이러한 다른 작업을 블록하고, 프레임 드롭을 일으킬 수 있다.

(Optimize JavaScript Execution 에서 인용)

React 는 Fiber 리팩토링을 통해 이 제어 불가능한 현황을 바꾸고, 인터랙션 경험을 더욱 향상시키기를 희망한다

P.S. Fiber 목표에 대한 더 많은 정보는, Codebase Overview 참조

이.중요한 특성

Fiber 의 중요한 특성은 다음과 같다:

  • 증분 렌더링 (렌더링 태스크를 블록으로 분할, 여러 프레임에 분산)

  • 업데이트 시 일시정지, 중지, 렌더링 태스크 재사용 가능

  • 다른 타입의 업데이트에 우선순위 부여

  • 병행 측면의 새로운 기초 능력

증분 렌더링은 프레임 드롭 문제를 해결하는 데 사용된다. 렌더링 태스크를 분할한 후, 매번一小節만 수행하고, 한 절 마치면 시간 제어권을 메인 스레드에 반환한다. 이전처럼 장시간 점유하지 않는다. 이 전략은 cooperative scheduling(협력적 스케줄링) 이라고 하며, 운영체제의 3 가지 태스크 스케줄링 전략 중 하나 (Firefox 도 실제 DOM 에 이 기술을 적용)

또한, React 자신의killer feature는 virtual DOM. 2 가지 이유:

  • UI 코딩이 간단해짐 (브라우저가 어떻게 해야 하는지 신경 쓰는 것이 아니라, 다음 UI 를 React 에게 전달)

  • DOM 이 virtual 할 수 있다면, 다른 (하드웨어, VR, native App) 도 가능

React 구현은 2 부분으로 나뉜다:

  • reconciler 어떤 시점의 전후 2 판 UI 의 차이를 찾음. 이전의 Stack reconciler 와 현재의 Fiber reconciler 포함

  • renderer 플러그인 방식, 플랫폼 관련 부분. React DOM, React Native, React ART, ReactHardware, ReactAframe, React-pdf, ReactThreeRenderer, ReactBlessed 등 포함

이번 파도는 reconciler 의 철저한 개조이며, killer feature 의 강화

삼.fiber 와 fiber tree

React 실행 시 존재하는 3 종류의 인스턴스:

DOM 실제 DOM 노드
-------
Instances React 가 유지하는 vDOM tree node
-------
Elements UI 가 어떻게 생겼는지 기술 (type, props)

Instances 는 Elements 에 기반하여 생성되며, 컴포넌트 및 DOM 노드의 추상 표현. vDOM tree 는 컴포넌트 상태 및 컴포넌트와 DOM 트리의 관계를 유지

첫 렌더링 프로세스 중에 vDOM tree 를 구축하고, 후속 업데이트가 필요할 때 (setState()), vDOM tree 를 diff 하여 DOM change 를 얻고, DOM change 를 DOM 트리에 적용 (patch)

Fiber 이전의 reconciler(Stack reconciler 라고 불림) 는 탑다운으로 재귀적으로mount/update하며, 중단 불가(메인 스레드를 계속 점유). 이로 인해 메인 스레드상의 레이아웃, 애니메이션 등의 주기적 태스크 및 인터랙션 응답이 즉시 처리되지 못해 경험에 영향

Fiber 가 이 문제를 해결하는思路는 렌더링/업데이트 프로세스 (재귀 diff) 를 일련의 작은 태스크로 분할하는 것. 매번 트리의一小部分을 체크하고, 마치면 다음 태스크를 계속할 시간이 있는지 확인. 있으면 계속, 없으면 자신을 일시정지하고, 메인 스레드가 한가할 때 계속

증분 업데이트에는 더 많은 컨텍스트 정보가 필요. 이전의 vDOM tree 는 분명히 만족시키기 어려워, fiber tree(Fiber 컨텍스트의 vDOM tree) 를 확장. 업데이트 프로세스는 입력 데이터 및 기존 fiber tree 에 기반하여 새로운 fiber tree(workInProgress tree) 를 구축하는 프로세스. 따라서, Instance 레이어에 이러한 인스턴스가 추가:

DOM
    실제 DOM 노드
-------
effect
    각 workInProgress tree 노드 위에 effect list 가 있음
    diff 결과를 저장하는 데 사용
    현재 노드 업데이트 완료 시 effect list 를 위에 merge(queue 가 diff 결과 수집)
- - - -
workInProgress
    workInProgress tree 는 reconcile 프로세스 중에 fiber tree 에서 구축된 현재 진행 스냅샷. 브레이크포인트 복구에 사용
- - - -
fiber
    fiber tree 는 vDOM tree 와 유사. 증분 업데이트에 필요한 컨텍스트 정보를 기술하는 데 사용
-------
Elements
    UI 가 어떻게 생겼는지 기술 (type, props)

주의:점선 위의 2 층은 모두 일시적인 구조. 업데이트 시에만 유용하고, 일상에는 계속 유지하지 않음.effect란 side effect 를 말하며, 진행될 예정인 DOM change 포함

fiber tree 상의 각 노드의 주요 구조 (각 노드를fiber라고 함) 는 다음과 같다:

// fiber tree 노드 구조
{
    stateNode,
    child,
    return,
    sibling,
    ...
}

return 은 현재 노드 처리 완료 후, 누구에게 자신의 성과 (effect list) 를 제출해야 ���는지 나타냄

P.S. fiber tree 는 실제로는 단방향 링크드 리스트 (Singly Linked List) 트리 구조.react/packages/react-reconciler/src/ReactFiber.js 참조

P.S. 소 fiber 와 대 Fiber 에 주의. 전자는 fiber tree 상의 노드를 나타내고, 후자는 React Fiber 를 나타냄

사.Fiber reconciler

reconcile 프로세스는 2 개의 페이즈 (phase) 로 나뉜다:

  1. (중단 가능) render/reconciliation workInProgress tree 를 구축하여 change 를 얻음

  2. (중단 불가) commit 이러한 DOM change 를 적용

render/reconciliation

fiber tree 를 청본으로 삼고, 각 fiber 를 작업 유닛으로 하여, 탑다운으로 노드마다workInProgress tree(구축 중인 새 fiber tree) 를 구축

구체적인 프로세스는 다음과 같다 (컴포넌트 노드를 예로):

  1. 현재 노드가 업데이트를 필요로 하지 않으면, 직접 자식 노드를 clone 하여 5 로 점프. 업데이트하려면 tag 를 붙임

  2. 현재 노드 상태 업데이트 (props, state, context 등)

  3. shouldComponentUpdate() 호출, false면, 5 로 점프

  4. render() 호출하여 새로운 자식 노드를 얻고, 자식 노드에 fiber 생성 (생성 프로세스는 기존 fiber 를 최대한 재사용. 자식 노드 증감도 여기서 발생)

  5. child fiber 가 생성되지 않았으면, 이 작업 유닛 종료. effect list 를 return 에 머지하고, 현재 노드의 sibling 을 다음 작업 유닛으로 함. 아니면 child 를 다음 작업 유닛으로 함

  6. 나머지 이용 가능 시간이 없으면,次回 메인 스레드가 한가할 때를 기다려 다음 작업 유닛 시작. 아니면, 즉시 시작

  7. 다음 작업 유닛이 없으면 (workInProgress tree 의 루트 노드로 돌아감), 제 1 페이즈 종료. pendingCommit 상태 진입

실제로는 1-6 의작업 루프. 7 은 출구. 작업 루프는 매번 한 가지만 수행하고, 마치면 숨 고를지 확인. 작업 루프 종료 시, workInProgress tree 의 루트 노드身上的 effect list 가 수집한 모든 side effect(하나 마칠 때마다 위에 머지하므로)

따라서, workInProgress tree 를 구축하는 프로세스는 diff 의 프로세스.requestIdleCallback을 통해 한 세트의 태스크 실행을 스케줄링. 하나의 태스크를 완료할 때마다 돌아와서 끼어듦 (더 긴급한) 이 있는지 확인. 한 세트의 태스크를 완료할 때마다, 시간 제어권을 메인 스레드에 반환하고,次回requestIdleCallback 콜백을 기다려 workInProgress tree 구축 계속

P.S. Fiber 이전의 reconciler 는 Stack reconciler 라고 불림. 이러한 스케줄링 컨텍스트 정보가 시스템 스택에 의해 저장되기 때문. 이전에는 한꺼번에 완료하고, 스택을 강조해도 의미가 없지만, 이름을 붙이는 것은 Fiber reconciler 와 구분하기 쉽게 하기 위함

requestIdleCallback

메인 스레드에 알림, 한가할 때 알려달라고. 별로 급하지 않은 일을 몇 개 하고 싶음

구체적인 사용법은 다음과 같다:

window.requestIdleCallback(callback[, options])
// 示例
let handle = window.requestIdleCallback((idleDeadline) => {
    const {didTimeout, timeRemaining} = idleDeadline;
    console.log(`超时了吗?${didTimeout}`);
    console.log(`可用时间剩余${timeRemaining.call(idleDeadline)}ms`);
    // do some stuff
    const now = +new Date, timespent = 10;
    while (+new Date < now + timespent);
    console.log(`花了${timespent}ms 搞事情`);
    console.log(`可用时间剩余${timeRemaining.call(idleDeadline)}ms`);
}, {timeout: 1000});
// 输出结果
// 超时了吗?false
// 可用时间剩余 49.535000000000004ms
// 花了 10ms 搞事情
// 可用时间剩余 38.64ms

주의, requestIdleCallback 스케줄링은 매끄러운 경험을 실현하기를 희망할 뿐, 절대적으로 무엇을 보증할 수 있는 것은 아니다. 예를 들어:

// do some stuff
const now = +new Date, timespent = 300;
while (+new Date < now + timespent);

搞事情 (React 중 라이프사이클 함수 등 시간상 React 제어 외의 것을 대응) 에 300ms 들이면, 어떤 메커니즘도 매끄러움을 보증할 수 없음

P.S. 일반적으로 나머지 이용 가능 시간은 10-50ms 정도. 스케줄링 공간은 그다지 넉넉하지 않음

commit

제 2 페이즈는 직접 한꺼번에 완료:

  1. effect list 처리 (3 종류 처리 포함: DOM 트리 업데이트, 컴포넌트 라이프사이클 함수 호출 및 ref 등 내부 상태 업데이트)

  2. 對終了。제 2 페이즈 종료. 모든 업데이트가 DOM 트리에 commit 됨

주의, 정말로한꺼번에 완료(동기 실행, 멈춤을 외칠 수 없음) 함. 이 페이즈의 실제 작업량은 비교적 큼. 따라서, 마지막 3 개 라이프사이클 함수 안에서 무거운 일을 하지 않도록

라이프사이클 hook

라이프사이클 함수도 2 개의 페이즈로 나뉨:

// 제 1 페이즈 render/reconciliation
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate

// 제 2 페이즈 commit
componentDidMount
componentDidUpdate
componentWillUnmount

제 1 페이즈의 라이프사이클 함수는여러 번 호출될 수 있음. 기본적으로 low 우선도 (뒤에서 소개하는 6 종류 우선도 중 하나) 로 실행. 고우선도 태스크에 중단되면, 나중에 재실행

오.fiber tree 와 workInProgress tree

더블 버퍼 기술 (double buffering). [redux 중의nextListeners](/articles/redux 源码解读/#articleHeader7) 처럼. fiber tree 를 주로, workInProgress tree 를 부로 함

더블 버퍼가 구체적으로 지칭하는 것은 workInProgress tree 구축 완료. 얻어지는 것이 새로운 fiber tree. 그리고 새 것을 좋아하고 옛 것을 싫어함 (current 포인터를 workInProgress tree 에 향하고, 옛 fiber tree 를 버림)

이렇게 하는 이점:

  • 내부 오브젝트 (fiber) 재사용 가능

  • 메모리 할당, GC 의 시간 오버헤드 절약

각 fiber 위에alternate 속성이 있으며, 이 또한 하나의 fiber 를 가리킴. workInProgress 노드 생성 시 우선적으로alternate 를 취득. 없으면 생성:

let workInProgress = current.alternate;
if (workInProgress === null) {
  //...这里很有意思
  workInProgress.alternate = current;
  current.alternate = workInProgress;
} else {
  // We already have an alternate.
  // Reset the effect tag.
  workInProgress.effectTag = NoEffect;

  // The effect list is no longer valid.
  workInProgress.nextEffect = null;
  workInProgress.firstEffect = null;
  workInProgress.lastEffect = null;
}

주석이 가리키는 대로, fiber 와 workInProgress 는 서로 참조를 보유. 「새 것을 좋아하고 옛 것을 싫어함」후, 옛 fiber 는 새 fiber 업데이트의예비 스페이스로 기능. fiber 인스턴스 재사용 목적 달성

P.S. 소스코드 안에는 몇 가지 재미있는 테크닉이 있음. 예를 들어 tag 의 비트 연산

육.우선도 전략

각 작업 유닛 실행 시 6 종류 우선도:

  • synchronous 이전의 Stack reconciler 조작과 같음. 동기 실행

  • task next tick 전에 실행

  • animation 다음 프레임 전에 실행

  • high 가까운 장래에 즉시 실행

  • low 조금 지연 (100-200ms) 실행해도 괜찮음

  • offscreen次回 render 시 또는 scroll 시에 실행

synchronous 는 수평 (첫 렌더링) 용. 가능한 한 빠르게. UI ���레드를 블록하는지 여부는 신경 쓰지 않음. animation 은requestAnimationFrame을 통해 스케줄링. 이로 인해 다음 프레임에서 즉시 애니메이션 프로세스 시작 가능. 뒤 3 개는 모두requestIdleCallback콜백에 의해 실행. offscreen 은 현재 숨겨진, 화면 밖의 (보이지 않는) 요소를 가리킴

고우선도의 예를 들어 키보드 입력 (즉시 피드백을 얻고 싶음). 저우선도의 예를 들어 네트워크 요청. 댓글을 표시하는 등. 또한, 긴급한 이벤트는 끼어듦을 허용

이러한 우선도 메커니즘에는2 가지 문제가 존재:

  • 라이프사이클 함수는 어떻게 실행하는가 (빈번히 중단될 수 있음): 트리거 순서, 횟수가 보증되지 않음

  • starvation(저우선도 굶어죽음): 고우선도 태스크가 많으면, 저우선도 태스크는 실행 기회가 전혀 없음 (굶어죽음)

라이프사이클 함수의 문제에는 공식 예가 있음:

low A
componentWillUpdate()
---
high B
componentWillUpdate()
componentDidUpdate()
---
restart low A
componentWillUpdate()
componentDidUpdate()

제 1 의 문제는 해결 중 (아직 해결하지 않음). 라이프사이클의 문제는 일부 기존 App 을 파괴하고, 매끄러운 업그레이드에 어려움을 가져옴. Fiber 팀은 우아한 업그레이드 경로를 찾기 위해 노력 중

제 2 의 문제는 완료한 조작의 재사용 (reusing work where it can) 을 통해 완화. 들리는 바로는 해결 방법을 모색 중

이들 2 가지 문제 자체는 해결이 그다지 좋지 않음. 어느 정도 해결하는가의 문제. 예를 들어 제 1 의 문제. 컴포넌트 라이프사이클 함수에 부작용이 너무 많으면, 무사히 해결할 방법이 없음. 이러한 문제들은 Fiber 업그레이드에 일정의 저항을 가져오지만, 결코 풀 수 없는 것은 아님 (한 발 양보하여, 새 기능이 충분한 매력이 있으면, 제 1 의 문제는 각자가 방법을 찾으면 해결됨)

칠.정리

알고 있음

React 는 일부 응답 경험 요구가 높은 장면에서 적용하지 않음. 예를 들어 애니메이션, 레이아웃, 제스처

근본적인 원인은 렌더링/업데이트 프로세스가 시작되면 중단할 수 없고, 메인 스레드를 계속 점유. 메인 스레드는 JS 실행에 바빠, 다른 것을 돌볼 겨를이 없음 (레이아웃, 애니메이션). 프레임 드롭, 지연 응답 (심지어 무응답) 등의 좋지 않은 경험을 초래

구함

메인 스레드 장시간 점유 문제를 철저히 해결할 수 있는 메커니즘. 눈앞의 문제에 대응할 수 있을 뿐만 아니라, 장원한 의미도 있음

The "fiber" reconciler is a new effort aiming to resolve the problems inherent in the stack reconciler and fix a few long-standing issues.

풀이

렌더링/업데이트 프로세스를 작은 블록 태스크로 분할. 합리적인 스케줄링 메커니즘을 통해 시간을 제어 (더 세밀한 입자, 더 강한 제어력)

그러면, 5 가지 하위 문제에 직면:

1.무엇을 분할하는가? 무엇이 분할할 수 없는가?

렌더링/업데이트 프로세스를 2 개의 페이즈 (diff + patch) 로 분할:

1.diff ~ render/reconciliation
2.patch ~ commit

diff 의 실제 작업은prevInstancenextInstance의 상태를 비교하여, 차이 및 대응하는 DOM change 를 찾아냄. diff 는 본질적으로 몇 가지 계산 (주사, 비교). 분할 가능 (절반 계산하고 나중에 계속 계산)

patch 페이즈는 이번 업데이트 중의 모든 DOM change 를 DOM 트리에 적용. 일련의 DOM 조작. 이러한 DOM 조작은 겉보기에는 분할 가능한 것 같지만 (change list 에 따라 한 단 한 단 수행), 이렇게 하면 한편으로 DOM 실제 상태와 유지하는 내부 상태가 불일치할 가능성이 있고,另外 경험에도 영향. 또한, 일반 장면 하에서, DOM 업데이트의耗时는 diff 및 라이프사이클 함수耗时에 비해 별것 아님. 분할의 의미는 그다지 크지 않음

따라서, render/reconciliation 페이즈의 작업 (diff) 은 분할 가능. commit 페이즈의 작업 (patch) 은 분할 불가

P.S. diff 와 reconciliation 은ただ의 대응 관계. 동가가 아님. 만약 구분할 필요가 있다면, reconciliation 은 diff 를 포함:

This is a part of the process that React calls reconciliation which starts when you call ReactDOM.render() or setState(). By the end of the reconciliation, React knows the result DOM tree, and a renderer like react-dom or react-native applies the minimal set of changes necessary to update the DOM nodes (or the platform-specific views in case of React Native).

(Top-Down Reconciliation 에서 인용)

2.어떻게 분할하는가?

먼저 대충 몇 가지 diff 작업 분할 방안을 생각:

  • 컴포넌트 구조로 분할. 잘 나눌 수 없음. 각 컴포넌트 업데이트의 작업량을 예측할 수 없음

  • 실제 공정으로 분할. 예를 들어getNextState(), shouldUpdate(), updateState(), checkChildren()로 나누고 몇 가지 라이프사이클 함수를 끼움

컴포넌트로 분할하는 것은 너무 굽. 분명히 대컴포넌트에 대해 불공평. 공정으로 분할하는 것은 너무 세밀함. 태스크가 너무 많음. 빈번한 스케줄링은 이득이 안 됨. 그렇다면 적절한 분할 단위가 있는가?

있음. Fiber 의 분할 단위는 fiber(fiber tree 상의 하나의 노드). 실제로는가상 DOM 노드로 분할. fiber tree 는 vDOM tree 에 기반하여 구축된 것. 트리 구조는一模一样. 노드가 지닌 정보만 차이

따라서, 실제로는 vDOM node 입자의 분할 (fiber 를 작업 유닛으로). 각 컴포넌트 인스턴스와 각 DOM 노드 추상 표현의 인스턴스는 모두 하나의 작업 유닛. 작업 루프 중, 매번 하나의 fiber 를 처리. 처리 완료 후, 전체 작업 루프를 중단/일시정지 가능

3.어떻게 태스크를 스케줄링하는가?

2 부분으로 나눔:

  • 작업 루프

  • 우선도 메커니즘

작업 루프는기본적인 태스크 스케줄링 메커니즘. 작업 루프 중에는 매번 하나의 태스크 (작업 유닛) 를 처리. 처리完畢 후에 한 숨 돌릴 기회가 있음:

// Flush asynchronous work until the deadline runs out of time.
while (nextUnitOfWork !== null && !shouldYield()) {
  nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}

shouldYield 는 시간을 다 썼는지 확인 (idleDeadline.timeRemaining()). 다 쓰지 않았으면, 다음 태스크를 계속 처리. 다 썼으면, 종료하고 시간 제어권을 메인 스레드에 반환하고,次回requestIdleCallback 콜백을 기다려 계속:

// If there's work left over, schedule a new callback.
if (nextFlushedExpirationTime !== NoWork) {
  scheduleCallbackWithExpiration(nextFlushedExpirationTime);
}

즉, (돌발 사건을 고려하지 않는) 정상적인 스케줄링은 작업 루프에 의해 완료. 기본규칙은: 각 작업 유닛 종료 시 다음 태스크를 할 시간이 있는지 확인. 시간이 없으면 먼저「일시정지」

우선도 메커니즘은 돌발 사건과 최적 차서를 처리하는 데 사용. 예를 들어:

  • commit 페이즈가 되면, 우선도를 올림

  • 고우 태스크를 절반 하다가 오류가 나면, 우선도를 내림

  • 짬을 내어 저우 태스크에 주목. 굶어죽지 않게

  • 대응 DOM 노드가此刻 불가시이면, 최저 우선도로 내림

이러한 전략들은 태스크 스케줄링을 동적으로 조정하는 데 사용. 작업 루프의보조 메커니즘. 가장 먼저 가��� 중요한 일을 수행

4.어떻게 중단/브레이크포인트 복구하는가?

중단: 현재 처리 중인 작업 유닛을 체크. 현재 성과 (firstEffect, lastEffect) 를 저장. tag 를 수정하여 마크. 신속하게 마무리하고 또 하나의requestIdleCallback을 시작.次回 기회가 있으면 계속

브레이크포인트 복구:次回 이 작업 유닛을 처리할 때, tag 가 중단된 태스크인지 확인. 미완료 부분을 계속 또는 재시도

P.S. 시간을 다 써서「자연」중단되든, 고우 태스크에 난폭하게 중단되든, 중단 메커니즘에게는 같음

5.어떻게 태스크 결과를 수집하는가?

Fiber reconciliation 의 작업 루프는 구체적으로 다음과 같다:

  1. 루트 노드의 우선도가 가장 높은 workInProgress tree 를 찾아, 그 대기 처리 노드 (컴포넌트 또는 DOM 노드를 나타냄) 를 취득

  2. 현재 노드가 업데이트를 필요로 하는지 확인. 필요 없으면, 직접 4 로

  3. 마크 (tag 를 붙임). 자신을 업데이트 (컴포넌트는props, context 등을 업데이트. DOM 노드는 DOM change 기록). 아이들에게 workInProgress node 생성

  4. 자식 노드가 생성되지 않았으면, effect list(DOM change 포함) 를 부모에 머지

  5. 아이 또는 형제를 대기 처리 노드로 삼고, 다음 작업 루프에 들어갈 준비. 대기 처리 노드가 없으면 (workInProgress tree 의 루트 노드로 돌아감), 작업 루프 종료

각 노드 업데이트 종료 시위에 effect list 를 머지하여 태스크 결과를 수집. reconciliation 종료 후, 루트 노드의 effect list 에 DOM change 를 포함한 모든 side effect 가 기록

유추

태스크가 분할 가능 (최종적으로 완전한 effect list 가 얻어지면 됨) 하면, 병행 실행(여러 Fiber reconciler + 여러 worker) 을 허용. 수평도 더 쉽게 블록 로드/렌더링 (vDOM 숲)

병행 렌더링의 경우, 소문에 따르면 Firefox 테스트 결과에 의하면, 130ms 의 페이지는 30ms 만으로搞定. 따라서 이 방면에서는 기대할 만함. React 는 이미 준비 완료. 이것이 React Fiber 컨텍스트에서 자주 듣는 대기unlock의 더 많은 특성 중 하나

팔.소스코드 간단 분석

15 에서 16 으로, 소스코드 구조에 큰 변화가 발생:

  • 더 이상mountComponent/updateComponent()가 보이지 않음. (beginWork/completeWork/commitWork()) 로 분할 재구성

  • ReactDOMComponent 도 삭제. Fiber 체계 하에서 DOM 노드 추상은 ReactDOMFiberComponent 로 나타냄. 컴포넌트는 ReactFiberClassComponent 로 나타냄. 이전에는 ReactCompositeComponent

  • Fiber 체계의 핵심 메커니즘은 태스크 스케줄링을 담당하는 ReactFiberScheduler. 이전의 ReactReconciler 에 해당

  • vDOM tree 가 fiber tree 로. 이전에는 위에서 아래로의 간단한 트리 구조. 현재는 단방향 링크드 리스트에 기반한 트리 구조. 유지하는 노드 관계가 더 많음

fiber tree 의 그림을 보고感受一下:

[caption id="attachment_1628" align="alignnone" width="970"]fiber-tree fiber-tree[/caption]

실제로 조금 생각해보면, Stack reconciler 에서 Fiber reconciler 로, 소스코드 레벨에서는재귀를 루프로 변경한 것뿐 (물론, 실제로 한 일은 재귀를 루프로 변경하는 것뿐만 아니라, 이는 첫 걸음)

总之, 소스코드 변화는 매우 큼. Fiber 思路를 사전에了解하지 않으면, 소스코드를 보는 것은 비교적艱難(React[15-] 의 소스코드를 본 적이 있으면, 더 迷惑하기 쉬움)

P.S. 이 清明 플로우차트 는正式로 퇴역

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성