「完整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.render,ReactDOM.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接受type、props、children,然后进行一些操作:

  • 处理props,从props中提取出key和ref
  • 处理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 逻辑,它会同时对 props 和 state 的变化前和变化后的值进行浅对比 ,如果都没发生变化则会跳过重渲染,相当于多了一层 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'));
相关推荐
用户270507772524几秒前
MCP 最小 Server 实测:3 个 JSON-RPC 报错与修法
前端
用户499335025072 分钟前
一人VIP账号多人共用:Web 代理里的会话共享怎么实现
前端
用户老李4 分钟前
无线电自学教程 05:第一台收音机——从矿石机到 SDR
前端
IPIPD11 分钟前
Node.js请求遇到503怎么重试?先解析Retry-After,再做有上限退避
前端
葡萄城技术团队18 分钟前
dsh-spreadjs-editor 的集成链路:把 SpreadJS Designer 嵌进 Agent 工作台
前端
FlyWIHTSKY1 小时前
vue2和vue3有什么区别,用法上有哪些区别
前端·javascript·vue.js
码云之上1 小时前
10 颗一模一样的「Edit」:中后台 Web Agent 怎么找到对的那一行
前端·人工智能·浏览器
量化分析码农2 小时前
【Python量化多因子实战 04】因子暴露不是 alpha:市值与行业中性化实战
前端·后端
不可能片场2 小时前
Electron 发布 正文写入的三条通道与读回口径
前端·electron
jerrywus2 小时前
让 AI 帮你部署 Vercel,报错了也不用手动搬日志
前端·agent·mcp