React 源码解析之总览

总览

总览

本小书大部分内容来自作者 Jokcy 的 《React 源码解析》: https://react.jokcy.me/

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

感谢 Jokcy 让我深度了解 React。如他所说,在决定阅读 React 源码时认为不会是一件很难的事,但是真正开始阅读之后才发现,事情没那么简单,因为需要足够的耐心、能够独立思考和静下心来(因为你会碰到之前编码没有见过的写法和概念等等)。

平常我们对外(后端、产品或其他前端)总喜欢说用的是 React 框架,可是我们并都熟悉 React 内部是怎么运行的。事实上,当 Facebook 将 React 和 ReactDOM 分包发布后,React 就不仅仅是前端框架了,15版本后 react 源码越来越少,而 React-dom 却很大。很显然,react 的很多逻辑都移到 react-dom 中了。

版本说明

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

先说下 React16 这个版本节点吧。

React16 较之前的版本是核心上的一次重写(想想就疯狂),虽然之前 API 没有变化继续使用,但同时也增加了很多好用的功能(不然不是白瞎了么)。这也是首次引入 Fiber 概念,之后新的功能都是围绕 Fiber,比如 AsyncModeProfiler 等。

说明:后面章节贴代码的部分,我都会删除原来英文注释,加上自己的理解,有问题的地方,还请在评论中指出(如果有评论的话,没有评论可以加我,有朋自远方来...)

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

...

上面代码我们选择性的去解析,无需所有都要去了解(个人学习方法方式)。

先说 Children

这个对象提供了一些处理 props.children 方法,children 是一个类似数组但又不是数组的数据结构,对其进行处理时可用 React.Children 外挂方法。

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} />
  }
}

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; // 多了一个标识

export { Component, PureComponent };

这两个类基本相同,唯一区别是 PureComponent 的原型上多了一个标识 isPureReactComponent

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 源码解析之唠叨两句

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 1、React新特性介绍 React v16.0 render 支持返回数组和字符串、Error Boundari...
    做最棒的阅读 4,706评论 0 4
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,039评论 2 35
  • 自己最近的项目是基于react的,于是读了一遍react的文档,做了一些记录(除了REFERENCE部分还没开始读...
    潘逸飞阅读 3,331评论 1 10
  • 上次写了react整体框架的理解,这次想写写看对于新版React的新的React Fiber的实现。 在React...
    离开North阅读 1,579评论 1 2
  • 他,是言格,是甄意的言格。 她,是甄意,是言格的甄意。 他,有自闭症,他的整个世界都是无声的,都是灰色的。在...
    Forever0阅读 2,742评论 1 4