「完整FX」React18内核探秘:手写React高质量源码迈向高阶开发

React18内核探秘:手写React高质量源码迈向高阶开发

核心代码,注释必读

// download:3w ukoou com

React 和 ReactDOM 各自负责什么 react 负责描述特性,提供React API。

类组件、函数组件、hooks、contexts、refs...这些都是React特性,而 react 模块只描述特性长什么样、该怎么用,并不负责特性的具体实现。

react-dom 负责实现特性。

react-dom、react-native 称为渲染器,负责在不同的宿主载体上实现特性,达到与描述相对应的真实效果。比如在浏览器上,渲染出DOM树、响应点击事件等。

ReactDOM.render 的输入------ ReactElement

javascript 复制代码
import React from 'react';
import ReactDOM from "./ReactDOM";
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

上面是一段常见的 React 代码。在项目的入口,人为显示地调用ReactDOM.renderReactDOM.render 接受 "根组件实例"和"挂载节点",然后进行内部逻辑转换,最终将DOM树渲染到挂载节点上。

那么,ReactDOM.render拿到的 "根组件实例" 具体是什么?

组件实例其实是一个对象,以children属性关联组件的父子关系。由React.createElement函数创建。

ReactElement 是 React.createElement 的输出,ReactDOM.render的输入,是 react 和 react-dom 之间最直观的联系。那么,我们来扒一扒这个数据结构。

我们一般会用JSX来描述组件结构,JSX本质上是一种语法扩展,通过Babel编译最终生成下面的语句:

css 复制代码
React.createElement(
  type,
  [props],
  [...children]
)

JSX最终将对组件的描述转换为对React.createElement的调用。React.createElement做了什么?
React.createElement接受typepropschildren,然后进行一些操作:

  • 处理props,从props中提取出keyref
  • 处理children,将children以单体或者数组的形式附加到props
  • 返回一个符合 ReactElement 数据结构的对象

如果用TypeScript简单描述 ReactElement 数据结构,它长这样👇

**

typescript 复制代码
interface ReactElement {
  $$typeof: Symbol | number; // 标识该对象是React元素,REACT_ELEMENT_TYPE = symbolFor('react.element') || 0xeac7,用Symbol获得一个全局唯一值
  type: string | ReactComponent | ReactFragment
  key: string | null
  ref: null  | string | object
  props: {
    [propsName: string]: any
      children?: ReactElement | Array<ReactElement>
  },
  _owner: {
    current:  null | Fiber
  }
}

React18内核探秘:手写React高质量源码迈向高阶开发 - PureComponent应用案例

React.PureComponent 类似于我们常用的 React.Component,区别在于 PureComponent 的内置 shouldComponentUpdate 逻辑,它会同时对 propsstate 的变化前和变化后的值进行浅对比 ,如果都没发生变化则会跳过重渲染,相当于多了一层 props 对比;下面通过一个简单的例子来对比这两种组件的效果差异;

效果对比

假设有一个计数器,点击按钮增加计数,并用两种组件渲染计数值:

scala 复制代码
class Counter extends React.Component {
  state = { count: 0 };

  render() {
    const { count } = this.state;
    return (
      <div style=>
        <div>count: {count}</div>
        <CountText count={count > 2 ? count : 0} />
        <ConstText count={count > 2 ? count : 0} />
        <button onClick={() => this.setState({ count: count + 1 })}>Add</button>
      </div>
    );
  }
}

// 普通组件
class CountText extends React.Component {
  render() {
    const { count } = this.props;
    console.log('normal rendered', count);
    return <div>normal: {count}</div>;
  }
}

// "纯"组件
class ConstText extends React.PureComponent {
  render() {
    const { count } = this.props;
    console.log('pure rendered', count);
    return <div>pure: {count}</div>;
  }
}
javascript 复制代码
import React from './react';
import ReactDOM from './react-dom';

class Greeting extends React.PureComponent {
  render() {
    console.log("Greeting was rendered at", new Date().toLocaleTimeString());
    return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>;
  }
}

// const Greeting = React.memo(function Greeting({ name }) {
//   console.log("Greeting was rendered at", new Date().toLocaleTimeString());
//   return <h3>Hello{name && ', '}{name}!</h3>;
// });


class MyApp extends React.Component {
  constructor(props){
    super(props)
    this.state = {name: '', address: ''}
  }

  setName = (newName) => {
    this.setState({name: newName})
  }
  setAddress = (newAddress) => {
    this.setState({address: newAddress})
  }
  render(){
    return <div>
      <label>
        Name{': '}
        <input onInput={e => {
          this.setName(e.target.value)
        }} />
      </label>
      <label>
        Address{': '}
        <input onInput={e => {
          this.setAddress(e.target.value)
        }} />
      </label>
      <Greeting name={this.state.name} />
    </div> 
  };
}

ReactDOM.render(<MyApp />, document.getElementById('root'));
相关推荐
程序员小杰@34 分钟前
✨WordToCard使用分享✨
前端·人工智能·开源·云计算
larntin20021 小时前
vue2开发者sass预处理注意
前端·css·sass
Enti7c1 小时前
利用jQuery 实现多选标签下拉框,提升表单交互体验
前端·交互·jquery
SHUIPING_YANG2 小时前
在Fiddler中添加自定义HTTP方法列并高亮显示
前端·http·fiddler
互联网搬砖老肖2 小时前
Web 架构之前后端分离
前端·架构
水银嘻嘻3 小时前
web 自动化之 selenium+webdriver 环境搭建及原理讲解
前端·selenium·自动化
寧笙(Lycode)3 小时前
为什么使用Less替代原始CSS?
前端·css·less
m0_zj3 小时前
57.[前端开发-前端工程化]Day04-webpack插件模式-搭建本地服务器
前端·webpack·node.js
GoFly开发者3 小时前
GoFly企业版框架升级2.6.6版本说明(框架在2025-05-06发布了)
前端·javascript·vue.js
qq_392794483 小时前
前端缓存踩坑指南:如何优雅地解决浏览器缓存问题?
前端·缓存