React 源碼解析之總覽

總覽

總覽

本小書大部分內(nèi)容來自作者 Jokcy 的 《React 源碼解析》: https://react.jokcy.me/

本文已同步在我的博客: http://ruizhengyun.cn/blog/post/2cb2c6f.html

感謝 Jokcy 讓我深度了解 React。如他所說,在決定閱讀 React 源碼時(shí)認(rèn)為不會(huì)是一件很難的事,但是真正開始閱讀之后才發(fā)現(xiàn),事情沒那么簡(jiǎn)單,因?yàn)樾枰銐虻哪托摹⒛軌颡?dú)立思考和靜下心來(因?yàn)槟銜?huì)碰到之前編碼沒有見過的寫法和概念等等)。

平常我們對(duì)外(后端、產(chǎn)品或其他前端)總喜歡說用的是 React 框架,可是我們并都熟悉 React 內(nèi)部是怎么運(yùn)行的。事實(shí)上,當(dāng) Facebook 將 React 和 ReactDOM 分包發(fā)布后,React 就不僅僅是前端框架了,15版本后 react 源碼越來越少,而 React-dom 卻很大。很顯然,react 的很多邏輯都移到 react-dom 中了。

版本說明

排版本小書的 React 版本是 V16.8.6

先說下 React16 這個(gè)版本節(jié)點(diǎn)吧。

React16 較之前的版本是核心上的一次重寫(想想就瘋狂),雖然之前 API 沒有變化繼續(xù)使用,但同時(shí)也增加了很多好用的功能(不然不是白瞎了么)。這也是首次引入 Fiber 概念,之后新的功能都是圍繞 Fiber,比如 AsyncModeProfiler 等。

說明:后面章節(jié)貼代碼的部分,我都會(huì)刪除原來英文注釋,加上自己的理解,有問題的地方,還請(qǐng)?jiān)谠u(píng)論中指出(如果有評(píng)論的話,沒有評(píng)論可以加我,有朋自遠(yuǎn)方來...)

V16.8.6 代碼

看看截止目前為止,React 暴露出來的 API

// react-16.8.6/packages/react/index.js
'use strict';
const React = require('./src/React');
module.exports = React.default || React;

// react-16.8.6/packages/react/src/React.js
...

const React = {
  // packages/react/src/ReactChildren.js
  Children: {
    map,
    forEach,
    count,
    toArray,
    only,
  },

  // packages/react/src/ReactCreateRef.js
  createRef,
  // packages/react/src/ReactBaseClasses.js
  Component,
  PureComponent,

  // packages/react/src/ReactContext.js
  createContext,
  // packages/react/src/forwardRef.js
  forwardRef,
  lazy,
  memo,

  // packages/react/src/ReactHooks.js
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,

  // packages/shared/ReactSymbols.js
  Fragment: REACT_FRAGMENT_TYPE,
  StrictMode: REACT_STRICT_MODE_TYPE,
  Suspense: REACT_SUSPENSE_TYPE,

  // packages/react/src/ReactElementValidator.js
  createElement: __DEV__ ? createElementWithValidation : createElement,
  cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
  createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
  isValidElement: isValidElement,

  // packages/shared/ReactVersion.js
  version: ReactVersion,

  // packages/shared/ReactSymbols.js
  unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE,
  unstable_Profiler: REACT_PROFILER_TYPE,

  // packages/react/src/ReactSharedInternals.js
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};

...

上面代碼我們選擇性的去解析,無需所有都要去了解(個(gè)人學(xué)習(xí)方法方式)。

先說 Children

這個(gè)對(duì)象提供了一些處理 props.children 方法,children 是一個(gè)類似數(shù)組但又不是數(shù)組的數(shù)據(jù)結(jié)構(gòu),對(duì)其進(jìn)行處理時(shí)可用 React.Children 外掛方法。

createRef

ref 用法,不推薦使用 string ref用法,比如 <div ref="divRef" />。那正確姿勢(shì)是怎樣的呢?請(qǐng)看

class App extends React.Component {
  constructor() {
    this.ref = React.createRef();
  }

  render() {
    return <div ref={this.ref} />
    // or
    return <div ref={node => this.ref = node} />
  }
}

ComponentPureComponent

packages/react/src/ReactBaseClasses.js 代碼

// Component
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {}
Component.prototype.forceUpdate = function(callback) {}

// ComponentDummy => Component仿制品
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;

// PureComponent
function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true; // 多了一個(gè)標(biāo)識(shí)

export { Component, PureComponent };

這兩個(gè)類基本相同,唯一區(qū)別是 PureComponent 的原型上多了一個(gè)標(biāo)識(shí) isPureReactComponent

if (ComponentExample.prototype && ComponentExample.prototype.isPureReactComponent) {
  return (
    !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
  );
}

這是檢查組件是否需要更新的一個(gè)判斷,ComponentExample 是你聲明的繼承自 ComponentPureComponent 的類,他會(huì)判斷你是否繼承自 PureComponent,如果是的話就用 shallowEqual 比較 stateprops

By the way(順便說一下,允許我騷一下):React 中對(duì)比一個(gè) ClassComponent 是否需要更新,只有兩個(gè)地方。

一是看有沒有 shouldComponentUpdate 方法;二就是這里的 PureComponent 判斷;

createContext

createContext 是官方定稿的 context 方案,在這之前我們一直在用的老的 context API ,也是 React 不推薦的 API。Now(現(xiàn)在),新的 API 出來了,官方也已經(jīng)確定在 17 大版本會(huì)把老 API 去除。

新 API 的使用方法

const { Provider, Consumer } = React.createContext('defaultValue');

const ProviderComp = props => (
  <Provider value='realValue'>
    {props.children}
  </Provider>
);

const ConsumerComp = () => (
  <Consumer>
    {value => <p>{value}</p>}
  </Consumer>
)

具體差異,后面講 context 環(huán)節(jié)會(huì)詳細(xì)指出。

forwardRef

forwardRef 是用來解決 HOC 組件傳遞 ref 的問題的,所謂 HOC 就是 Higher Order Component。就拿redux 來說,使用 connect 來給組件綁定需要的 state,這其中其實(shí)就是給我們的組件在外部包了一層組件,然后通過 ...props 的方式把外部的 props 傳入到實(shí)際組件。forwardRef 的使用方法如下:

const TargetComponent = React.forwordRef((props, ref) => {
  <TargetComponent ref={ref} {...props} />
});

這也說明了為什么要提供 createRef 作為新的 ref 使用方法的原因,如果用 string ref 就沒法當(dāng)作參數(shù)傳遞了。

后面章節(jié)會(huì)詳細(xì)分析 ref

lazy

是用來實(shí)現(xiàn)異步加載模塊的功能。

memo

是一個(gè)高階函數(shù),它與 React.PureComponent類似,但是一個(gè)函數(shù)組件而非一個(gè)類。

useXXX 系列

這就是 React16 的 Hooks 了,后續(xù)會(huì)做代碼分解。

類型

Fragment: REACT_FRAGMENT_TYPE,
StrictMode: REACT_STRICT_MODE_TYPE,
Suspense: REACT_SUSPENSE_TYPE,
unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE,
unstable_Profiler: REACT_PROFILER_TYPE,

這 5 個(gè)都是 React 提供的組件,但他們呢其實(shí)都只是占位符,都是一個(gè) Symbol,在 React 實(shí)際檢測(cè)到他們的時(shí)候會(huì)做一些特殊的處理,比如 StrictModeAsyncMode 會(huì)讓他們的子節(jié)點(diǎn)對(duì)應(yīng)的 Fiber 的 mode 都變成和他們一樣的mode

元素

createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,

createElement

是 React 輸出中最重要的 API 了,是用來創(chuàng)建 ReactElement 的,但是很多前端童鞋卻從沒見過,也沒用過,這是為什么呢?這就得感謝 JSX 了,我們知道 JSX 并不是標(biāo)準(zhǔn)的 js,所以要經(jīng)過編譯才能變成可運(yùn)行的 js,而編譯之后,createElement 就出現(xiàn)了:

// jsx
<div id="app">content</div>

// js
React.createElement('div', { id: 'app' }, 'content')

cloneElement

它就很明顯了,是用來克隆一個(gè) ReactElement

createFactory

它是用來創(chuàng)建專門用來創(chuàng)建某一類 ReactElement 的工廠的

export function createFactory(type) {
  const factory = createElement.bind(null, type);
  factory.type = type;
  return factory;
}

其實(shí)就是綁定了第一個(gè)參數(shù)的 createElement,一般我們用 JSX 進(jìn)行編程的時(shí)候不會(huì)用到這個(gè) API。

isValidElement

是用來驗(yàn)證是否是一個(gè) ReactElement 的,基本也用不太到。

你還可以

上一篇:React 源碼解析之嘮叨兩句

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評(píng)論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評(píng)論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評(píng)論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,779評(píng)論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評(píng)論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評(píng)論 0 291
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評(píng)論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,515評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,750評(píng)論 1 375
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,933評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評(píng)論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,492評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,703評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • 1、React新特性介紹 React v16.0 render 支持返回?cái)?shù)組和字符串、Error Boundari...
    做最棒的閱讀 4,743評(píng)論 0 4
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,099評(píng)論 2 35
  • 自己最近的項(xiàng)目是基于react的,于是讀了一遍react的文檔,做了一些記錄(除了REFERENCE部分還沒開始讀...
    潘逸飛閱讀 3,453評(píng)論 1 10
  • 上次寫了react整體框架的理解,這次想寫寫看對(duì)于新版React的新的React Fiber的實(shí)現(xiàn)。 在React...
    離開North閱讀 1,613評(píng)論 1 2
  • 他,是言格,是甄意的言格。 她,是甄意,是言格的甄意。 他,有自閉癥,他的整個(gè)世界都是無聲的,都是灰色的。在...
    Forever0閱讀 2,808評(píng)論 1 4