React源码解析-React 导出了些什么

React 用了好久,是不是发现自己依然不懂 React 实现原理?只知道它的基本用法,弄不懂 React 内部流程?
本系列文章基于 React v16.8.6,每周分享2-3篇精华文章,每篇文章都是 React 中的一个关键点。欢迎评论交流。

React 导出的对象详解

直接复制的代码,在代码上面打的注释


var React = {
     

  // ----------------------- Children 处理 ---------------------------
  // 和 props.children 相关的工具函数
  // props.children 不是一个严格的数组,所以添加了类似数组的工具
  Children: {
     

    // 类似array.map
    map: mapChildren,

    // 类似 array.forEach
    forEach: forEachChildren,

    // 计算 prop.children 的个数,计算直接子节点的个数
    // children 为一个时不是数组,就是一个单独的 react.element
    // 有多个时,是一个数组,Array.isArray(children) 为 true
    // children 个数为0时, children 为 undefined
    count: countChildren,

    // 将 children 变为数组
    // children 为 undefined 变成空数组
    // 将单个的变为只有一个元素的数组
    // 将是数组的 children 变成数组,差别是数组中每个元素的 key,toArray 处理之后变成 .0 .1 .2 这种标识性的 key
    toArray: toArray,

    // 如果只有一个直接子节点,则返回它
    // 没有直接子节点或者有多个都会报错 React.Children.only expected to receive a single React element child.
    only: onlyChild
  },
  // ---------------------------------------------------------------

  // ------------------------- Component -----------------------------
  // 是一个函数,执行之后返回 { current: null },
  // 用于  其中 r = React.createRef()
  createRef: createRef,

  // React.Component 最常用
  // 它是一个构造函数,同时在它的原型上定义类一些方法
  // isReactComponent
  // setState
  // forceUpdate
  Component: Component,

  // 继承自 Component,它也是一个构造函数
  // 它的原型上有一个属性 isPureReactComponent = true
  PureComponent: PureComponent,
  // ------------------------------------------------------------------

  // React 中跨组件数据传递方案,避免中间元素传递 props 带来的不必要麻烦
  // Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
  createContext: createContext,
  
  // 创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中
  // React.forwardRef 接受渲染函数作为参数。
  // React 将使用 props 和 ref 作为参数来调用此函数。此函数应返回 React 节点。
  forwardRef: forwardRef,
  
  // ----------------------------- 性能优化 ------------------------
  // 配合 Suspense,实现异步加载组件,可以有效减少首次加载的包体积
  // 返回一个异步加载的组件
  lazy: lazy,
  
  // 优化函数式组件性能的
  // 类似于 PureComponent,渲染下面这个组件,不管怎样更改 param 对象,都不会重新render
  // const Test = React.memo(
  // (props) => 
{props.param.count}
,
// (prevProps, nextProps) => true) memo: memo, // --------------------------------------------------------------- error: error, warn: warn, // -------------------- React hooks 系列 -------------------- // hooks 可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性 // 类似于 setState // const [a, setA] = useState(aInitialValue) // aInitialValue a 的初始值 // a 是每次 setA 执行之后的最新值,setA 类似于 this.setState,参数可以是函数或者其他 useState: useState, // 接收一个包含命令式、且可能有副作用代码的函数。 // 赋值给 useEffect 的函数会在组件渲染到屏幕之后执行, // 也就是可以将改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作放到 useEffect 里面执行 // useEffect 函数需返回一个清除函数,在每次 rerender 之前执行,可以将清除定时器、取消订阅的时间放到返回的函数里面 // const r = useRef() // useEffect(() => { // r.current = setTimeout(xx, 1000); // return () => { clearTimeout(r.current) } //}) useEffect: useEffect, // useReducer: useReducer, // useRef 返回一个被 Object.seal 处理过的 {current: null} 对象,即对象的 key 属性不可更改 // 可以用于 ref 访问 dom 如
// 也可用于存值 // 如 const r = useRef(),r.current = setTimeout(xx, 1000), clearTimeout(r.current) useRef: useRef, // ------------ function component 性能优化工具 ---------------- // 返回一个被缓存的函数,只有在依赖数组中的变量变化的时候才会重新生成一个新的函数,可用于避免子组件重新渲染,提高性能 // 返回自己定义的函数,惰性执行 useCallback: useCallback, // 惰性求值,它仅会在某个依赖项改变时才重新计算 memoized 值,返回的是自己定义的值 useMemo: useMemo, // ------------------------------------------------------------- // 在 function component 中直接 useContext(MyContext) 便可以获取到最近的 Provider 的 value // 不需要 Consumer useContext: useContext, // 可以让你在使用 ref 时自定义暴露给父组件的实例值,与 forwardRef 一起使用 // function FancyInput(props, ref) { // const inputRef = useRef(); // useImperativeHandle(ref, () => ({ // focus: () => { // inputRef.current.focus(); // } // })); // return ; // } // FancyInput = forwardRef(FancyInput); useImperativeHandle: useImperativeHandle, // 可用于在 React 开发者工具中显示自定义 hook 的标签 useDebugValue: useDebugValue, // 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect // function useLayoutEffect(effect: EffectCallback, inputs?: InputIdentityList): void; // function useEffect(effect: EffectCallback, inputs?: InputIdentityList): void; useLayoutEffect: useLayoutEffect, // ------------------------------------------------------------ // Symbol(react.fragment),允许你将子列表分组,而无需向 DOM 添加额外节点 Fragment: REACT_FRAGMENT_TYPE, // Symbol.for('react.profiler') React 性能相关 Profiler: REACT_PROFILER_TYPE, // StrictMode 不会渲染任何可见的 UI, // 是一个用来突出显示应用程序中潜在问题的工具, 检查过时的 api,识别不安全的生命周期,检测意外的副作用 StrictMode: REACT_STRICT_MODE_TYPE, // 配合 lazy 实现懒加载 // 可以指定加载指示器(loading indicator),以防其组件树中的某些子组件尚未具备渲染条件 // const OtherComponent = React.lazy(() => import('./OtherComponent')); // }> Suspense: REACT_SUSPENSE_TYPE, // ------------------------- Element ------------------------------------- // createElement(type, config, children) // 依据 type 来创建一个新的 element,这是 React 使用非常频繁的功能,JSX 就是就是转化成了这个函数 // {childen} // {$$typeof: Symbol(react.element), key:"1",props:{name:"lxfriday",children:{...}},ref:null} createElement: createElementWithValidation, // cloneElement(element, config, children) // 以 element 元素为样板克隆并返回新的 React 元素 // 返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。新的子元素将取代现有的子元素,而来自原始元素的 key 和 ref 将被保留。 // React.cloneElement() 几乎等同于:{children} cloneElement: cloneElementWithValidation, // 核心语句是下面两条: // var validatedFactory = createElementWithValidation.bind(null, type); // validatedFactory.type = type; // 可以用 createElement 替代 createFactory: createFactoryWithValidation, // 验证对象是否为 React 元素,返回值为 true 或 false isValidElement: isValidElement, // 导出 React 版本 version: ReactVersion, unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals };

React 常见问题

包含 JSX 的代码中为什么一定需要导入 React

来看看编译前的代码

<div ref={
     r}>
  <span>1</span>
</div>

babel 编译之后的代码

React.createElement("div", {
     
  ref: r
}, React.createElement("span", null, "1"));

从编译后的代码可以看到,需要有一个 React对象,JSX 编译之后就是用的 React.createElement 来创建 render 树,所以 React 导入是必须的。

class-component 中定义的 function 为什么要绑定 this

以下为可能的三种情形

  • handleClick 没有绑定 this,打印 undefined,原因是 handleClick 是以赋值的方式被赋予 onClick,所以默认的 this 会失效,类似于 display = foo.display; display(); this 不是 foo
  • handleBindClick1 用箭头函数绑定 this,而且它的 this,不会被变更,打印了 TestThis
  • handleBindClick2,用 bind(this) 绑定上下文,打印了 TestThis
class TestThis extends Component {
     
  handleBindClick1 = () => {
     
    console.log('handleBindClick1', this);
  }
  handleClick() {
     
    console.log('handleClick', this);
  }
  handleBindClick2() {
     
   console.log('handleBindClick2', this); 
  }
  render() {
     
    return (
      <div>
        <button onClick={
     this.handleClick}>click not bind </button>
        <button onClick={
     this.handleBindClick1}>bind 1 click</button>
        <button onClick={
     this.handleBindClick2.bind(this)}>bind 2 click</button>
      </div>
    );
  }
}

React源码解析-React 导出了些什么_第1张图片
要注意的是,用箭头函数定义的定义的函数式作为 上下文的属性存在,而以普通函数方式定义的函数则是出现在 TestThis 的原型上

ref 有哪几种形式

function ref

<Page ref={
     r => this.page = r}>xxx</Page>

object ref

const r = useRef(); // r = {current: null}
<Page ref={
     r}>xxx</Page>

string ref(不推荐)

<Page ref="page">xxx</Page>

this.refs.page.xxx

关注公众号 云影sky,即送技术资料,您的支持是我最大的动力

你可能感兴趣的:(React源码,React,源码解析)