React源码解析
一直在用react进行编码,下面来看一下react框架的源码,了解一下react框架的思路。
首先,看下packages/react文件夹下的代码,也就是React
通过packages/react/index.js,可以大致了解到有哪些常用的react api
export {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
act as unstable_act,
Children,
Component,
Fragment,
Profiler,
PureComponent,
StrictMode,
Suspense,
SuspenseList,
cloneElement,
createContext,
createElement,
createFactory,
createMutableSource,
createRef,
createServerContext,
experimental_use,
forwardRef,
isValidElement,
lazy,
memo,
............
useId,
useCallback,
useContext,
useDebugValue,
useDeferredValue,
useEffect,
useImperativeHandle,
useInsertionEffect,
useLayoutEffect,
useMemo,
useMutableSource,
useSyncExternalStore,
useReducer,
useRef,
useState,
useTransition,
version,
} from './src/React';
这边把自己常用的来进行分析讲解
1. Component, pureComponent
在进行类组件编写时,第一步就是进行该构造函数的继承
question: 什么时候要使用pureComponent? pureComponent有什么注意事项吗?
class TestCom extends React.Component
or
class TestCom extends React.PureComponent
源码如下:
// import {Component, PureComponent} from './ReactBaseClasses'; function Component(props, context, updater) { this.props = props; this.context = context; this.refs = emptyObject; // {} // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } //上面代码中,constructor()、toString()、toValue()这三个方法,其实都是定义在Component.prototype上面。 //eg: 在继承时,使用class引入,写法更加清晰 class TestCom extends React.Component { constructor (props) { // 构造方法,没写的时候默认执行 super(props) // 调用父类的constructor(props) // constructor默认返回实例对象(this) // 在super之后才能使用this,例如this.state } }
ReactNoopUpdateQueue是一个对象,上面有一些方法,是用于对一些错误信息得提示
ReactNoopUpdateQueue = { isMounted: function(publicInstance) { return false; }, enqueueForceUpdate: function(publicInstance, callback, callerName) { warnNoop(publicInstance, 'forceUpdate'); }, enqueueReplaceState: function(){}, enqueueSetState: function() {} } warnNoop() { // 进行一些错误信息得提示 const constructor = publicInstance.constructor; const componentName = (constructor && (constructor.displayName || constructor.name)) || 'ReactClass'; const warningKey = `${componentName}.${callerName}`; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { return; } console.error(..........); didWarnStateUpdateForUnmountedComponent[warningKey] = true; }
在Component原型上绑定了setState, forceUpdate方法
setState
Component.prototype.setState = function(partialState, callback) { this.updater.enqueueSetState(this, partialState, callback, 'setState'); }; // this.setState({name: 'test'}) enqueueSetState: function( publicInstance, partialState, // 需要更新得state -> name: 'test' callback, callerName, ) { // 这里没有对partialState进行更新 // 源码注释也进行了说明:不能保证对 `setState` 的调用会同步运行,因为它们最终可能会被批量处理,因为您的函数可能在 receiveProps 之后调用 // shouldComponentUpdate,这个新的 state、props 和 context 还不会分配给这个 // ps: 但是并不是这里对于new state更新 warnNoop(publicInstance, 'setState'); }, setState(updater[, callback]) //setState() 的第二个参数为可选的回调函数,它将在 setState 完成合并并重新渲染组件后执行 this.setState((state, props) => { // state,props接受得都保证为最新 return {counter: state.counter + props.step}; });
上述对于setState的代码感觉并没有发现对于机制的执行,全局搜索时,发现有enqueueSetState,是什么时候执行的,起的什么作用呢
const classComponentUpdater = { enqueueSetState(inst, payload, callback) { const fiber = getInstance(inst); // key._reactInternals const eventTime = requestEventTime(); // 获取时间 const lane = requestUpdateLane(fiber); } } // requestUpdateLane() 事件队列 export function requestUpdateLane(fiber: Fiber): Lane { }
forceUpdate:对于组件的强制更新
Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); }; enqueueForceUpdate: function(publicInstance, callback, callerName) { warnNoop(publicInstance, 'forceUpdate'); }, // 当状态更新时,但未调用setState, 这不会调用`shouldComponentUpdate`,但会调用`componentWillUpdate` 和 `componentDidUpdate`
question : 重点:这里并没有进行一些其他的操作,只是调用warnNoop,是怎么进行了功能的实现?
-----------------------------------------------------------------------------待回答----------------------------------------------
PureComponent
function PureComponent(props, context, updater) { // 与Component相同 } const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); pureComponentPrototype.constructor = PureComponent; // 将Component的原型赋值给PureComponent,并且将其constructor属性更改为PureComponent // 避免这些方法的额外原型跳转 assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
Component与PureComponent类基本相同,唯一的区别是PureComponent上多了一个标识isPureReactComponent
// react-reconciler
// checkShouldComponentUpdate() // 检查组件是否需要更新, checkShouldComponentUpdate(.....) { if (ctor.prototype && ctor.prototype.isPureReactComponent) { // 对于PureComponent而言,当state和props未更新时,组件不重新渲染 return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); } return true; }
但是在使用PureComponent 过程中,避免出现props中引用类型,虽然props发生变化但地址未变,导致虽然props更新但组件未更新的bug.
export { REACT_FRAGMENT_TYPE as Fragment } export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); //Symbol(react.fragment) 只是一个占位符?
export function createContext<T>(defaultValue: T): ReactContext<T> { const context: ReactContext<T> = { $$typeof: REACT_CONTEXT_TYPE, _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, }; return context; }
4.createRef
React.createRef 创建一个能够通过 ref 属性附加到 React 元素的 ref。
export function createRef(): RefObject { // 创建一个具有current:null 属性的对象 const refObject = { current: null, }; if (__DEV__) { Object.seal(refObject); } return refObject; }
5.memo
React.memo为高阶组件
你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现
export function memo<Props>( type: React$ElementType, compare?: (oldProps: Props, newProps: Props) => boolean, ) { return { // $$typeof属性是为了虚拟dom的安全性 // 使用Symbol标记每个React元素,减少xss攻击 $$typeof: REACT_MEMO_TYPE, // Symbol.for('react.memo'); type, compare: compare === undefined ? null : compare, }; }
6. useCallback与useMemo
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
export function useCallback<T>( callback: T, deps: Array<mixed> | void | null, ): T { const dispatcher = resolveDispatcher(); return dispatcher.useCallback(callback, deps); } export function useMemo<T>( create: () => T, deps: Array<mixed> | void | null, ): T { const dispatcher = resolveDispatcher(); return dispatcher.useMemo(create, deps); } const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
export function useCallback<T>( callback: T, deps: Array<mixed> | void | null, ): T { const dispatcher = resolveDispatcher(); return dispatcher.useCallback(callback, deps); } const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
7.createElement
react中JSX在编译时会被Babel编译为React.createElement方法
即使文件中没使用其他引用,在文件开头也需要引入 import React from 'react'
来看一下相关createElement的源码
export function createElement(type, config, children) { // type-> 元素类型 config -> 元素配置(一些类名等) children -> 子元素(该元素下的子元素) let propName; // Reserved names are extracted const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { // 将config处理后赋值给props // .......... } // 子对象可以是多个参数,它们被转移到新分配的props对象上。 const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } props.children = childArray; } // Resolve default props if (type && type.defaultProps) { // ............ } } return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); }
看了一下,很多地方还是没有真正的理解,等后面再读一遍的时候再进行整理补充吧
待续
浙公网安备 33010602011771号