React 源碼解析之總覽

日常咱們對外(後端、產品或其餘前端)總喜歡說用的是 React 框架,但是咱們並不都熟悉 React 內部是怎麼運行的。事實上,當 Facebook 將 React 和 ReactDOM 分包發佈後,React 就不只僅是前端框架了,15版本後 react 源碼愈來愈少,而 React-dom 卻很大。很顯然,react 的不少邏輯都移到 react-dom 中了。javascript

版本說明

先說下 React16 這個版本節點吧。前端

React16 較以前的版本是核心上的一次重寫(想一想就瘋狂),雖然以前 API 沒有變化繼續使用,但同時也增長了不少好用的功能(否則不是白瞎了麼)。這也是首次引入 Fiber 概念,以後新的功能都是圍繞 Fiber,好比 AsyncModeProfiler 等。java

說明:後面章節貼代碼的部分,我都會刪除原來英文註釋,加上本身的理解,有問題的地方,還請在評論中指出(若是有評論的話,沒有評論能夠加我,有朋自遠方來...)node

上代碼

看看截止目前爲止,React 暴露出來的 APIreact

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

// react/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,
};

...
複製代碼

上面代碼咱們選擇性的去解析,無需全部都要去了解(我的學習方法方式)。編程

先說 Children

這個對象提供了一些處理 props.children 方法,children 是一個相似數組但又不是數組的數據結構,對其進行處理時可用 React.Children 外掛方法。redux

createRef

ref 用法,不推薦使用 string ref 用法,好比 <div ref="divRef" />。那正確姿式是怎樣的呢?後端

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

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

Component 和 PureComponent

packages/react/src/ReactBaseClasses.js 代碼數組

import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
const emptyObject = {};

// 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; // 多了一個標識

export { Component, PureComponent };
複製代碼

這兩個類基本相同,惟一區別是 PureComponent 的原型上多了一個標識 isPureReactComponentbash

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

這是檢查組件是否須要更新的一個判斷,ComponentExample 是你聲明的繼承自 ComponentPureComponent 的類,他會判斷你是否繼承自 PureComponent,若是是的話就用 shallowEqual 比較 stateprops

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

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

createContext

createContext 是官方定稿的 context 方案,在這以前咱們一直在用的老的 context API ,也是 React 不推薦的 API。Now(如今),新的 API 出來了,官方也已經肯定在 17 大版本會把老 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 環節會詳細指出。

forwardRef

forwardRef 是用來解決 HOC 組件傳遞 ref 的問題的,所謂 HOC 就是 Higher Order Component。就拿redux 來講,使用 connect 來給組件綁定須要的 state,這其中其實就是給咱們的組件在外部包了一層組件,而後經過 ...props 的方式把外部的 props 傳入到實際組件。forwardRef 的使用方法以下:

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

這也說明了爲何要提供 createRef 做爲新的 ref 使用方法的緣由,若是用 string ref 就無法看成參數傳遞了。

後面章節會詳細分析 ref

lazy

是用來實現異步加載模塊的功能。

memo

是一個高階函數,它與 React.PureComponent相似,可是一個函數組件而非一個類。

useXXX 系列

這就是 React16 的 Hooks 了,後續會作代碼分解。

類型

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 個都是 React 提供的組件,但他們呢其實都只是佔位符,都是一個 Symbol,在 React 實際檢測到他們的時候會作一些特殊的處理,好比 StrictModeAsyncMode 會讓他們的子節點對應的 Fiber 的 mode 都變成和他們同樣的mode

元素

createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,
複製代碼

createElement

是 React 輸出中最重要的 API 了,是用來建立 ReactElement 的,可是不少前端童鞋卻從沒見過,也沒用過,這是爲何呢?這就得感謝 JSX 了,咱們知道 JSX 並非標準的 js,因此要通過編譯才能變成可運行的 js,而編譯以後,createElement 就出現了:

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

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

cloneElement

它就很明顯了,是用來克隆一個 ReactElement

createFactory

它是用來建立專門用來建立某一類 ReactElement 的工廠的

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

其實就是綁定了第一個參數的 createElement,通常咱們用 JSX 進行編程的時候不會用到這個 API。

isValidElement

是用來驗證是不是一個 ReactElement 的,基本也用不太到。

你能夠...

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

下一篇:React 源碼解析之ReactElement

相關文章
相關標籤/搜索