日常咱们对外(后端、产品或其余前端)总喜欢说用的是 React 框架,但是咱们并不都熟悉 React 内部是怎么运行的。事实上,当 Facebook 将 React 和 ReactDOM 分包发布后,React
就不只仅是前端框架了,15版本后 react
源码愈来愈少,而 React-dom
却很大。很显然,react
的不少逻辑都移到 react-dom
中了。javascript
先说下 React16 这个版本节点吧。前端
React16 较以前的版本是核心上的一次重写(想一想就疯狂),虽然以前 API 没有变化继续使用,但同时也增长了不少好用的功能(否则不是白瞎了么)。这也是首次引入 Fiber
概念,以后新的功能都是围绕 Fiber
,好比 AsyncMode
和 Profiler
等。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} />
}
}
复制代码
看 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
的原型上多了一个标识 isPureReactComponent
bash
if (ComponentExample.prototype && ComponentExample.prototype.isPureReactComponent) {
return (
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
);
}
复制代码
这是检查组件是否须要更新的一个判断,ComponentExample
是你声明的继承自 Component
或 PureComponent
的类,他会判断你是否继承自 PureComponent
,若是是的话就用 shallowEqual
比较 state
和 props
。
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
是用来解决 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 实际检测到他们的时候会作一些特殊的处理,好比 StrictMode
和 AsyncMode
会让他们的子节点对应的 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
的,基本也用不太到。