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导入
相关推荐
天外来物30 分钟前
element-plus主题配置及动态切换主题
前端·css·element
晴殇i1 小时前
这个前端工具杀疯了!发布一周狂揽 10k Star,Snapchat 开源框架重新定义跨平台
前端·程序员
孟祥_成都1 小时前
打包票!前端和小白一定明白的人工智能基础概念!
前端·人工智能
小满zs2 小时前
Next.js第六章(平行路由)
前端
小满zs2 小时前
Next.js第七章(路由组)
前端
Mountain and sea2 小时前
发那科机器人指令详解:从入门到精通
前端·机器人
泯泷2 小时前
Tiptap 深度教程(四):终极定制 - 从零创建你的专属扩展
前端·javascript·架构
局i2 小时前
vue简介
前端·javascript·vue.js
yqcoder3 小时前
vue2 和 vue3 中,列表中的 key 值作用
前端·javascript·vue.js
U***49833 小时前
前端TypeScript教程汇总,从基础到高级
前端·javascript·typescript