「完整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'));
相关推荐
不认输的西瓜2 小时前
fetch-event-source源码解读
前端·javascript
用户39051332192882 小时前
前端性能杀手竟然不是JS?图片优化才是绝大多数人忽略的"降本增效"方案
前端
朱昆鹏3 小时前
开源 Claude Code + Codex + 面板 的未来vibecoding平台
前端·后端·github
lyrieek3 小时前
pgadmin的导出图实现,还在搞先美容后拍照再恢复?
前端
永远是我的最爱3 小时前
基于.NET的小小便利店前台收银系统
前端·sqlserver·.net·visual studio
从文处安3 小时前
「九九八十一难」第一难:前端数据mock指南(TS + VUE)
前端
Zhencode3 小时前
Vue3 响应式依赖收集与更新之effect
前端·vue.js
x-cmd3 小时前
[x-cmd] jsoup 1.22.1 版本发布,引入 re2j 引擎,让 HTML 解析更安全高效
前端·安全·html·x-cmd·jsoup
天下代码客4 小时前
使用electronc框架调用dll动态链接库流程和避坑
前端·javascript·vue.js·electron·node.js
weixin199701080164 小时前
【性能提升300%】仿1688首页的Webpack优化全记录
前端·webpack·node.js