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.

 
2.Fragment
export { REACT_FRAGMENT_TYPE as Fragment  }

export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); //Symbol(react.fragment) 只是一个占位符?

 

3. createContext
context提供了一种无需为每层组件手动添加props,就能在组件树间进行数据传递的方法
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,
  );
}

 

 看了一下,很多地方还是没有真正的理解,等后面再读一遍的时候再进行整理补充吧

待续

 

 

posted @ 2022-09-26 20:19  千亿昔  阅读(1556)  评论(0编辑  收藏  举报