React渲染原理学习笔记

React为什么要这么设计?

1. 什么是JSX?

问:JSX是什么?为什么React要使用它?

答: JSX是JavaScript XML的缩写,它是一种语法扩展,让我们能在JavaScript中编写类似HTML的代码。

javascript 复制代码
// JSX代码

const element = <h1 className="title">Hello, World!</h1>;

jsx

JSX让React组件的编写更直观,但它不是有效的JavaScript代码,需要被转换。


2. JSX转换的基本概念

问:JSX是如何被转换成JavaScript的?

答: JSX通过编译器(通常是Babel)转换成 React.createElement() 调用。

javascript 复制代码
// JSX代码

const element = <div id="container">Hello</div>;



// 转换后的JavaScript代码

const element = React.createElement(

  'div',

  { id: 'container' },

  'Hello'

);

jsx


3. React.createElement函数详解

问:React.createElement的参数****结构是怎样的?

答: React.createElement 接收三个主要参数:

go 复制代码
React.createElement(

  type,      // 元素类型(字符串或组件)

  props,     // 属性对象

  children   // 子元素(可多个)

);

JavaScript

示例分析:

less 复制代码
// JSX

<div className="header" style={{ color: 'red' }}>

  <span>Hello</span>

  World

</div>



// 转换结果

React.createElement(

  'div',

  {

    className: 'header',

    style: { color: 'red' }

  },

  React.createElement('span', null, 'Hello'),

  'World'

);

jsx


4. 组件类型的转换

问:自定义组件和HTML元素在转换时有什么区别?

答: 类型参数的处理方式不同:

javascript 复制代码
// HTML元素 - 使用字符串

const divElement = <div>Content</div>;

// → React.createElement('div', null, 'Content')



// 自定义组件 - 使用变量引用

function MyComponent() { return <div>Hello</div>; }

const compElement = <MyComponent prop="value" />;

// → React.createElement(MyComponent, { prop: 'value' })



// 点表示法组件

const Element = <App.Header title="Welcome" />;

// → React.createElement(App.Header, { title: 'Welcome' })

jsx


5. 属性转换的特殊情况

问:JSX属性名和DOM属性名有什么对应关系?

答: 有些属性名需要特殊转换:

css 复制代码
// class → className

<div className="container"><​/div>

// → React.createElement('div', { className: 'container' })



// for → htmlFor

<label htmlFor="inputId"><​/label>

// → React.createElement('label', { htmlFor: 'inputId' })



// style对象处理

<div style={{ color: 'red', fontSize: 16 }}><​/div>

// → React.createElement('div', { style: { color: 'red', fontSize: 16 } })

jsx


6. 子元素的处理

问:不同类型的子元素是如何处理的?

答: 子元素根据类型有不同的处理方式:

less 复制代码
// 文本子元素

<div>Hello World</div>

// → React.createElement('div', null, 'Hello World')



// 多个子元素

<div>

  <span>First</span>

  <span>Second</span>

</div>

// → React.createElement('div', null, 

//     React.createElement('span', null, 'First'),

//     React.createElement('span', null, 'Second')

//   )



// 表达式子元素

<div>{2 + 2}</div>

// → React.createElement('div', null, 2 + 2)



// 条件渲染

<div>{isVisible && <span>Visible</span>}</div>

// → React.createElement('div', null, isVisible && 

//     React.createElement('span', null, 'Visible')

//   )

jsx


7. 复杂结构的转换

问:更复杂的JSX结构是如何转换的?

答: 让我们看一个包含多种情况的例子:

css 复制代码
// 复杂JSX

<Container id="main">

  <Header user={currentUser} />

  {isLoading ? <Spinner /> : <Content data={data} />}

  <button onClick={handleClick} disabled={!isValid}>

    Click me

  </button>

</Container>



// 转换结果

React.createElement(

  Container,

  { id: 'main' },

  React.createElement(Header, { user: currentUser }),

  isLoading 

    ? React.createElement(Spinner, null)

    : React.createElement(Content, { data: data }),

  React.createElement(

    'button',

    {

      onClick: handleClick,

      disabled: !isValid

    },

    'Click me'

  )

);

jsx


8. Fragment的转换

问:React Fragment是如何转换的?

答: Fragment提供了一种分组子元素而不添加额外DOM节点的方式:

javascript 复制代码
// JSX Fragment

<​>

  <p>Paragraph 1</p>

  <p>Paragraph 2</p>

<​/>



// 转换结果(旧方式)

React.createElement(

  React.Fragment,

  null,

  React.createElement('p', null, 'Paragraph 1'),

  React.createElement('p', null, 'Paragraph 2')

);



// 或者使用新的jsx函数(React 17+)

_jsx(React.Fragment, {

  children: [

    _jsx('p', { children: 'Paragraph 1' }),

    _jsx('p', { children: 'Paragraph 2' })

  ]

});

jsx


9. 现代JSX转换(React 17+)

问:React 17引入的新JSX转换有什么不同

答: 新的转换方式不再需要手动引入React:

javascript 复制代码
// 传统转换(需要引入React)

import React from 'react';



function App() {

  return <h1>Hello World</h1>;

}



// 传统转换结果

import React from 'react';



function App() {

  return React.createElement('h1', null, 'Hello World');

}



// 新转换方式(不需要引入React)

function App() {

  return <h1>Hello World</h1>;

}



// 新转换结果

import { jsx as _jsx } from 'react/jsx-runtime';



function App() {

  return _jsx('h1', { children: 'Hello World' });

}

jsx


10. JSX转换的完整流程

问:从JSX到最终渲染的完整流程是怎样的?

答: 完整的转换和渲染流程:

scss 复制代码
JSX代码

    ↓ (编译时 - Babel)

React.createElement() 调用 或 _jsx() 调用

    ↓ (运行时 - React)

React元素对象(虚拟DOM)

    ↓ (协调过程 - ReactDOM)

DOM更新

Plain Text

虚拟DOM对象示例:

yaml 复制代码
// React.createElement('div', { id: 'test' }, 'Hello')

{

  $$typeof: Symbol(react.element),

  type: 'div',

  key: null,

  ref: null,

  props: {

    id: 'test',

    children: 'Hello'

  },

  _owner: null,

  _store: {}

}

JavaScript


11. 实际编译示例

问:能否展示一个真实项目的编译示例?

答: 让我们看一个完整的组件转换:

javascript 复制代码
// 源代码

import React, { useState } from 'react';



function Counter() {

  const [count, setCount] = useState(0);

  

  return (

    <div className="counter">

      <h1>Count: {count}</h1>

      <button onClick={() => setCount(count + 1)}>

        Increment

      </button>

    </div>

  );

}



// 编译后(简化版)

import React, { useState } from 'react';



function Counter() {

  const [count, setCount] = useState(0);

  

  return React.createElement(

    'div',

    { className: 'counter' },

    React.createElement(

      'h1',

      null,

      'Count: ',

      count

    ),

    React.createElement(

      'button',

      {

        onClick: () => setCount(count + 1)

      },

      'Increment'

    )

  );

}

jsx


总结

JSX转换的核心原理是将类似HTML的语法糖转换为 React.createElement 函数调用,这些调用会创建React元素对象(虚拟DOM),最终由React负责将这些对象渲染为真实的DOM。

关键要点:

  • JSX不是模板语言,而是JavaScript的语法扩展
  • 转换发生在编译时(通过Babel)
  • 每个JSX元素对应一个 React.createElement 调用
  • 属性、子元素都会作为参数传递给创建函数
  • React 17+的新转换方式自动处理JSX导入
相关推荐
OpenTiny社区3 小时前
我用3 分钟上手 RankProcessChart 排名进度图!
前端·github
十里八乡有名的后俊生3 小时前
从在线文档崩溃说起-我的前端知识管理系统搭建之路
前端·开源·github
_光光3 小时前
任务队列及大文件上传实现(前端篇)
前端·react.js·typescript
残冬醉离殇3 小时前
缓存与同步:前端数据管理的艺术
前端
前端西瓜哥3 小时前
常用的两种填充策略:fit 和 fill
前端
Lsx_3 小时前
ECharts 全局触发click点击事件(柱状图、折线图增大点击范围)
前端·javascript·echarts
不吃香菜的猪4 小时前
构建时变量注入:Vite 环境下 SCSS 与 JavaScript 的变量同步机制
前端·javascript·scss
代码哈士奇4 小时前
无界微前端学习和使用
前端·学习
一枚前端小能手4 小时前
🚀 Node.js 25重磅发布!快来看看吧
前端·javascript·node.js