React18.2.0源码解析1:React核心API
一般使用React常用的两个包:react、react-dom,其中react是核心包,无关平台,react-dom是web平台的render包。
react包中导出的API如下:
export {
/**
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, React内部使用,禁止外部使用
act as unstable_act
Children
Component
*/
Fragment,
/**
Profiler,
PureComponent
StrictMode,
*/
Suspense,
/**
SuspenseList,
cloneElement,
*/
createContext,
/**
createElement,
createFactory
createMutableSource,
createRef,
createServerContext,
*/
forwardRef,
/**
isValidElement,
*/
lazy,
memo,
startTransition,
/**
unstable_Cache,
unstable_DebugTracingMode,
unstable_LegacyHidden,
unstable_Offscreen,
unstable_Scope,
unstable_TracingMarker,
unstable_getCacheSignal,
unstable_getCacheForType,
unstable_useCacheRefresh,
useId,
*/
useCallback,
useContext,
/**
useDebugValue,
*/
useDeferredValue,
useEffect,
useImperativeHandle,
useInsertionEffect,
useLayoutEffect,
useMemo,
useMutableSource,
useSyncExternalStore,
useReducer,
useRef,
useState,
useTransition,
/**
version,
*/
} from './src/React';
上述代码注释掉的API不是重要的,会在最后大概说明用法。
Fragment
用于包裹没有父节点的多个元素,简写:<></>
<Fragment>
<div>hello</div>
<div>react</div>
</Fragment>
// 或者
<>
<div>hello</div>
<div>react</div>
</>
源码
/**
packages/react/src/React.js
*/
export {
...
REACT_FRAGMENT_TYPE as Fragment,
...
}
/**
packages/shared/ReactSymbols.js
*/
export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
从源码中可以看出,Fragment
只是一个类型,用于后续在reconciler(协调器)
和Fiber(常说的虚拟DOM)
判断是否为Fragment
从而进行相应的操作。
Suspense
用于组件懒加载展示loading,通常与React.lazy
一起使用
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
源码
/**
packages/react/src/React.js
*/
export {
...
REACT_SUSPENSE_TYPE as Suspense,
...
}
/**
packages/shared/ReactSymbols.js
*/
export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
createContext
创建一个context(上下文)
,用于组件间传递数据,通常和useContext
一起使用
const ThemeContext = createContext('dark')
const App = () => {
return (
<ThemeContext.Provider value="dark">
<SomeComponent />
</ThemeContext.Provider>
)
}
const SomeComponent = () => {
const theme = useContext(ThemeContext)
return <div>SomeComponent:{theme}</div>
}
源码
export function createContext<T>(defaultValue: T): ReactContext<T> {
const context: ReactContext<T> = {
// fiber类型
$$typeof: REACT_CONTEXT_TYPE,
// 作为支持多个并发渲染器的解决方法
// 一个渲染器为主渲染器,另一个渲染器为辅渲染器
// 最多有两个并发渲染器:
// React Native (primary) 和 Fabric (secondary)、
// React DOM (primary) 和 React ART (secondary)
// 主渲染器将上下文值存储到_currentValue中
// 辅渲染器将上下文值存储到_currentValue2中
_currentValue: defaultValue,
_currentValue2: defaultValue,
// 用于跟踪此上下文有多少并发渲染器
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
_defaultValue: (null: any),
_globalName: (null: any),
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
// 用于警告判断
let hasWarnedAboutUsingNestedContextConsumers = false;
let hasWarnedAboutUsingConsumerProvider = false;
let hasWarnedAboutDisplayNameOnConsumer = false;
// 开发环境做了一些处理
if(__DEV__){
...
}
else {
context.Consumer = context;
}
if (__DEV__) {
...
}
return context;
}
创建一个ReactContext
对象,提供Provider
和Consumer
组件
__DEV__
代码主要是用法错误提醒:<Context.Consumer.Provider> ➡️ <Context.Provider>
和<Context.Consumer.Consumer> ➡️ <Context.Consumer>
forwardRef
用于透传ref,也通过useImperativeHandle
将ref将组件内部的方法暴露。
const App = () => {
const ref = useRef()
useEffect(() => {
ref.current.onTest()
},[])
return <SomeComponent ref={ref} />
}
const SomeComponent = forwardRef((props,ref) => {
const onTest = () => {
console.log('SomeComponent test')
}
useImperativeHandle(ref,() => {
return {
onTest
}
},[])
return <div>SomeComponent</div>
})
源码
export function forwardRef<Props, ElementType: React$ElementType>(
render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
if (__DEV__) {
...
}
const elementType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
};
if (__DEV__) {
...
}
return elementType;
}
创建一个REACT_FORWARD_REF_TYPE
类型对象
__DEV__
代码主要是用法错误提醒:forwardRef(memo(...)) ➡️ memo(forwardRef(...))
、必须一个render function
、如果只有一个参数会提示是否遗忘了ref参数
、不支持propTypes或defaultProps属性
lazy
用于懒加载组件
源码
export function lazy<T>(
ctor: () => Thenable<{default: T, ...}>,
): LazyComponent<T, Payload<T>> {
const payload: Payload<T> = {
// We use these fields to store the result.
_status: Uninitialized,
_result: ctor,
};
const lazyType: LazyComponent<T, Payload<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer,
};
if (__DEV__) {
...
}
return lazyType;
}
创建一个REACT_LAZY_TYPE
类型对象
__DEV__
代码主要是用法错误提醒:不支持propTypes或defaultProps属性
memo
props未更改时跳过重新渲染组件,用于性能优化。
const SomeComponent = memo(() => {
return <div>SomeComponent</div>
})
源码
export function memo<Props>(
type: React$ElementType,
compare?: (oldProps: Props, newProps: Props) => boolean,
) {
if (__DEV__) {
...
}
const elementType = {
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare === undefined ? null : compare,
};
if (__DEV__) {
...
}
return elementType;
}
创建一个REACT_MEMO_TYPE
类型对象
__DEV__
代码主要是用法错误提醒:memo接受的参数必须时一个组件
startTransition
不阻塞UI的前提下更新state
const Counter = () => {
const [count,setCount] = useState(0)
const onAdd = () => {
startTransition(() => {
setCount(val => val + 1)
})
}
const onSub = () => {
startTransition(() => {
setCount(val => val - 1)
})
}
return (
<button onClick={onSub}>-</button>
<div>{count}</div>
<button onClick={onAdd}>+</button>
)
}
源码
/**
const ReactCurrentBatchConfig: BatchConfig = {
transition: null,
};
*/
import ReactCurrentBatchConfig from './ReactCurrentBatchConfig';
/**
* export const enableTransitionTracing = false;
*/
import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
export function startTransition(
scope: () => void,
options?: StartTransitionOptions,
) {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
const currentTransition = ReactCurrentBatchConfig.transition;
if (__DEV__) {
ReactCurrentBatchConfig.transition._updatedFibers = new Set();
}
if (enableTransitionTracing) {
if (options !== undefined && options.name !== undefined) {
ReactCurrentBatchConfig.transition.name = options.name;
ReactCurrentBatchConfig.transition.startTime = -1;
}
}
try {
scope();
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
if (__DEV__) {
if (prevTransition === null && currentTransition._updatedFibers) {
const updatedFibersCount = currentTransition._updatedFibers.size;
if (updatedFibersCount > 10) {
console.warn(
'Detected a large number of updates inside startTransition. ' +
'If this is due to a subscription please re-write it to use React provided hooks. ' +
'Otherwise concurrent mode guarantees are off the table.',
);
}
currentTransition._updatedFibers.clear();
}
}
}
}
代码可能看不太懂,怎么会在不阻塞UI的情况下更新state的,后面看到reconciler
就会明白的。
React hooks
useCallback
: 在组件重新渲染时缓存useContext
: 读取和订阅组件的contextuseDebugValue
: 调试值,仅用于开发环境useDeferredValue
: 延迟更新UI,useEffect
: 用于执行副作用,常用作函数组件的生命周期。useImperativeHandle
: 可通过ref将函数内部数据和函数暴露useInsertionEffect
: 适用于CSS-in-JS库使用useLayoutEffect
: 在浏览器重新绘制屏幕前触发。此hooks会影响性能,最好使用useEffect
useMemo
: 在重新渲染组件时缓存计算结果useMutableSource
: 创建一个可变源,参考: rfc147useSyncExternalStore
: 用于订阅外部数据源,参考:useSyncExternalStoreuseReducer
: 使用reduceruseRef
: 创建一个ref,可以引用一个不需要渲染的值useState
: 创建一个状态useTransition
: 不阻塞UI的情况下更新state
上述所有的hooks
源码都在packages/react/src/ReactHooks.js
下,其基本一致,都是封装ReactFiberHooks
的语法糖,仅列举useState
源码
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
其他
Profiler
测试React的渲染性能
<Profiler id="App" onRender={onRender}>
<App />
</Profiler>
源码
/**
packages/react/src/React.js
*/
export {
...
REACT_PROFILER_TYPE as Profiler,
...
}
/**
packages/shared/ReactSymbols.js
*/
export const REACT_PROFILER_TYPE = Symbol.for('react.profiler');
StrictMode
开启严格模式
<StrictMode>
<App />
</StrictMode>
源码
/**
packages/react/src/React.js
*/
export {
...
REACT_STRICT_MODE_TYPE as StrictMode,
...
}
/**
packages/shared/ReactSymbols.js
*/
export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
SuspenseList
用于React devtools
,具体参考:#19684
源码
/**
packages/react/src/React.js
*/
export {
...
REACT_SUSPENSE_LIST_TYPE as SuspenseList,
...
}
/**
packages/shared/ReactSymbols.js
*/
export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
createMutableSource
用于创建可变源,但是未在官网或CHANGELOG中找到任何描述,看着像应该删除的API。参考:#19948
export function createMutableSource<Source: $NonMaybeType<mixed>>(
source: Source,
getVersion: MutableSourceGetVersionFn,
): MutableSource<Source> {
const mutableSource: MutableSource<Source> = {
_getVersion: getVersion,
_source: source,
_workInProgressVersionPrimary: null,
_workInProgressVersionSecondary: null,
};
if (__DEV__) {
...
}
return mutableSource;
}
createServerContext
创建服务端上下文,但是未在官网或CHANGELOG中找到任何描述。
export function createServerContext<T: ServerContextJSONValue>(
globalName: string,
defaultValue: T,
): ReactServerContext<T> {
if (!enableServerContext) {
throw new Error('Not implemented.');
}
let wasDefined = true;
if (!ContextRegistry[globalName]) {
wasDefined = false;
const context: ReactServerContext<T> = {
$$typeof: REACT_SERVER_CONTEXT_TYPE,
_currentValue: defaultValue,
_currentValue2: defaultValue,
_defaultValue: defaultValue,
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
_globalName: globalName,
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
if (__DEV__) {
...
}
ContextRegistry[globalName] = context;
}
const context = ContextRegistry[globalName];
if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
context._defaultValue = defaultValue;
if (
context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED
) {
context._currentValue = defaultValue;
}
if (
context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED
) {
context._currentValue2 = defaultValue;
}
} else if (wasDefined) {
throw new Error(`ServerContext: ${globalName} already defined`);
}
return context;
}
弃用/过时 API
其中,有一部分已经弃用的API:createFactory
其中,在React最新文档中标记为过时API的有:
Child
: 用于传递组件内渲染的元素,可以使用children
属性进行传递,详情参见:备选方案cloneElement
: 基于元素创建新元素,可以使用类似renderItem
方法传入到组件内,详情参见:备选方案Component
:通过类创建React组件,可以使用函数组件,详情参见:备选方案createElement
: 用于创建React元素,一般不会使用此方法,都是使用JSX创建的。createRef
: 创建ref对象,一般用于类组件,如果是函数组件请使用useRef
,详情参见:备选方案isValidElement
:用于判断是否为React元素,通常配合createElement
PureComponent
: 用于类组件优化性能用的,跳过相同的props重复渲染
还有其中使用unstable_
开头的都是表示不稳定的API,也不建议使用。
jsx
在React17
之前jsx文件需要导入React
其很大一部分原因就是因为jsx
函数,此函数是将JSX
代码转换成jsx
函数,在React17
之前叫做createElement
。
源码
/**
* https://github.com/reactjs/rfcs/pull/107
*/
export function jsx(type, config, maybeKey) {
let propName;
const props = {};
let key = null;
let ref = null;
// 将key转化成string
if (maybeKey !== undefined) {
if (__DEV__) {
checkKeyStringCoercion(maybeKey);
}
key = '' + maybeKey;
}
// 将key转化成string
if (hasValidKey(config)) {
if (__DEV__) {
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
if (hasValidRef(config)) {
ref = config.ref;
}
// 将config对象的属性转化到props对象
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
// 加载defaultProps
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
undefined,
undefined,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
if (__DEV__) {
...
}
return element;
};
从源码中可以看出,最终实际只是创建一个REACT_ELEMENT_TYPE
类型的对象,其最终创建的代码是在render
包中完成的,web平台的是react-dom
。
总体上看,React
的源码参考价值不是特别大,因为基本上都是没有核心逻辑,都是封装其他包的代码.