【React源碼學習】Suspense && Children

Suspense

16.6 提供的一個feature,在線源碼地址:https://github.com/facebook/react/blob/master/packages/react/src/React.jsreact

在一個Suspense組件,它下面渲染了一個或者多個異步的組件,有任何一個組件throw了一個promise以後,在這個promise的resolved以前,都會顯示fallback中的內容。git

也就是說,在一個Suspense組件中,有多個組件,它要等它裏面全部組件都resolved以後,纔會撤銷掉fallback中的內容github

demoapi

// suspense/index.js
import React, { Suspense, lazy } from 'react'

const LazyComp = lazy(() => import('./lazy.js'))

let data = ''
let promise = ''
function requestData() {
  if (data) return data
  if (promise) throw promise
  promise = new Promise(resolve => {
    setTimeout(() => {
      data = 'Data resolved'
      resolve()
    }, 2000)
  })
  throw promise
}

function SuspenseComp() {
  const data = requestData()

  return <p>{data}</p>
}

export default () => (
  <Suspense fallback="loading data">
    <SuspenseComp />
    <LazyComp />
  </Suspense>
)

// suspense/lazy.js

import React from 'react'

export default () => <p>Lazy Comp</p>

源碼數組

// React.js

...
Suspense: REACT_SUSPENSE_TYPE // 仍是一個常量
...
// https://github.com/facebook/react/blob/master/packages/react/src/ReactLazy.js
import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';

import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';

export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
  return {
    $$typeof: REACT_LAZY_TYPE,
    _ctor: ctor,  // 傳進來的參數
    // React uses these fields to store the result.
    _status: -1, // 用來記錄當前thenable這個對象處於的狀態。當渲染到lazy component狀態的時候,會調用這個ctor,而後返回一個thenable對象,通常來講是一個promise,處於pending狀態,-1
    _result: null, // 用來記錄Thenable resolved以後返回的屬性,在lazy最終resolved出來的組件,放在裏面。後續咱們渲染lazy component的時候,直接拿result
  };
}

Children

先看一個demopromise

import React from 'react'

function ChildrenDemo(props) {
  console.log(props.children)
  // 若是React.Children.map 返回的是一個數組,它會繼續把它們展開成一維數組 [1, 1, 1, 2, 2, 2]
  console.log(React.Children.map(props.children, c => [c, [c, c]]))
  return props.children
}

export default () => (
  <ChildrenDemo>
    <span>1</span>
    <span>2</span>
  </ChildrenDemo>
)

在線源碼地址瀏覽器

https://github.com/facebook/react/blob/master/packages/react/src/ReactChildren.js(點擊進入)dom

圖解源碼流程異步

mapIntoWithKeyPrefixInternal性能

【它是一個大遞歸】,在此作了如下幾件事情:

1. 處理key
2. 在此作一個很是重要的事情,就是去context pool中去獲取一個context
3. traverseAllChildren
4. 後續全部流程完,會把這個context歸還

traverseAllChildren

在此作了如下幾件事情:

1. children爲null的狀況return 0
2. traverseAllChildrenImpl

traverseAllChildrenImpl

【它是一個小遞歸】,根據react element的type作了如下幾件事情:

  1. 對應符合規則中的幾種type,調用回調,即mapSingleChildIntoContext;
  2. 若是是數組,遞歸自身traverseAllChildrenImpl
  3. 若是是function,遞歸自身traverseAllChildrenImpl
  4. 若是是obejct, 進行相應處理

mapSingleChildIntoContext

作了如下幾個事情

  1. 從傳入進來的參數(pool context)中拿出func
  2. 調用func,並傳入節點
  3. 判斷func return的結果是否爲數組,若是是,大遞歸mapIntoWithKeyPrefixInternal。(注意:大遞歸mapIntoWithKeyPrefixInternal傳入的最後一個參數是c => c,直接返回這個節點。由於若是再次調用咱們傳入的方法,就會變成一個無限返回數組的遞歸)
  4. 若是3不成立,且返回的結果不爲null,則表示當前已經遍歷到了數據數組根節點。判斷結果是否合理,經過cloneAndReplaceKey返回了一個新的react element,新的key,其餘都相同,並在最終要map返回出去的數組中push了當前新的react element,到此結束

小結:源碼中運用了遞歸+資源池這樣一個概念去編寫相應邏輯。在此談下對象池,當一個方法多是一個常常要被調用的方法。若是該方法展開的比較多,聲明和釋放的對象多,這是一個很是須要性能的問題,尤爲是在瀏覽器計算的過程當中,可能須要去重複的去new 和delete一塊內存空間,避免內存計算形成的性能抖動,從而去節省一部分的開銷

memo

16.6 feature 目的是給function component也提供一個相似pure component的功能

源碼

...
export default function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  ...
  return {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };
}

從上面來看,最終返回了一個$$typeof爲REACT_MEMO_TYPE的對象,真正的處理邏輯還須要在react-dom中學習

fragment

const functionComponent = () => {
    return <> 
        <span>1</span>
    </>
}

<> 實際上是React.Fragment

源碼

Fragment: REACT_FRAGMENT_TYPE // 依然是一個常量,實際處理邏輯須要在react-dom中查看對此類型的處理

StrictMode

提供一些過期api的提醒

源碼

StrictMode: REACT_STRICT_MODE_TYPE // 依然是個symbol

cloneElement

在ReactElement.js源碼中,實現了對element進行了clone的邏輯

相關文章
相關標籤/搜索