react之jsx编译原理

简介: react之jsx编译原理

使用react的朋友想必对jsx的语法相当熟悉,简单点来说,就是JavaScript和html可以混着写,灵活快速,而且可以一目了然的看清楚DOM的结构,当然jsx需要经过babel编译为javascript对象,再经过渲染插入到页面上。
接下来我们来探讨几个问题:

react内部是如何处理JavaScript和html混写的代码?
组件名为啥首字母一定要大写?
在花括号{}里边内容,比如是一行表达式 var a = ‘hello world’; 为啥会报错?
下边来具体看看jsx处理逻辑,上例子:

1、demo01
const name = 'world';

hello, { name }

1
2
3
babel编译为:

var name = 'world';
React.createElement("h1", {
title: "title",
ref: "title"
}, "hello, ", name);
1
2
3
4
5
显然关键部分是 React.createElement, 看官网定义:

React.createElement(
type,
[props],
[...children]
)
1
2
3
4
5
作用是创建并返回指定类型的新React元素。其中的type类型参数既可以是标签名字符串(如上边demo1的h1标签),也可以是React组件类型(class组件或函数组件),或是React fragment类型。

第2个参数是props,上createElement源码,看看属性title,ref的处理:

react/src/ReactElement.js

...

const hasOwnProperty = Object.prototype.hasOwnProperty;

const RESERVED_PROPS = {
key: true,
ref: true,
self: true, source: true,
};

...

/**

  • Create and return a new ReactElement of the given type.
  • See https://reactjs.org/docs/react-api.html#createelement
    */
    export function createElement(type, config, children) {
    let propName;

    // Reserved names are extracted
    const props = {}; //用于存储元素属性

    let key = null; //存储key值
    let ref = null; //存储ref值
    let self = null;
    let source = null;

    if (config != null) {

     if (hasValidRef(config)) {
         ref = config.ref; //传入的config有ref属性且config.ref !== undefined,赋值给上边定义的变量ref
     }
     if (hasValidKey(config)) {
         key = '' + config.key; //传入的config有key属性且config.key !== undefined,赋值给上边定义的变量ref
     }
    
     self = config.__self === undefined ? null : config.__self;
     source = config.__source === undefined ? null : config.__source;
     // Remaining properties are added to a new props object
    
     //遍历传入的config判断属性不是对象原型链上的,也不属性RESERVED_PROPS(key/ref等),赋到上边定义的props对象上
    
     for (propName in config) {  
         if (
             hasOwnProperty.call(config, propName) &&
             !RESERVED_PROPS.hasOwnProperty(propName)
         ) {
             props[propName] = config[propName];
         }
     }
    

    }

    ...

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
结论:定义的属性ref,key会直接挂在生成元素上,而其他属性挂在props上

第3个参数是children,demo1的编译生成的"hello, ", name 对应的都是子元素children,源码处理如下:

// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
...
props.children = childArray;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
子元素可以是一个或者多个,都会被挂在props.children上,demo1的编译生成的"hello, ", name就是多个文本节点

demo1代码最终生成的虚拟DOM为为:

再经过ReactDOM.render()方法渲染到页面上

2、demo2
我们来看看当组件名首字母是小写的时候,为啥会报错

const Comp = () =>

hello, world

const App = () => {
return (

)
}
1
2
3
4
5
6
7
babel编译为:

var Comp = function Comp() {
return React.createElement("h1", null, "hello, world");
};

var App = function App() {
return React.createElement(Comp, null); //返回React组件类型元素
};
1
2
3
4
5
6
7
而将组件名Comp首字母改为小写comp,babel则编译为:

var comp = function comp() {
return React.createElement("h1", null, "hello, world");
};

var App = function App() {
return React.createElement("comp", null); //type为标签名字符串类型
};

1
2
3
4
5
6
7
8
显然html没有comp标签,故无法正常渲染

3、demo3
从demo1可知,花括号{}里边的内容也会被当做子元素处理,我们来看看某些不支持,或者编译不符合预期的写法

1)属性中输出javascript变量,不能加引号,不然会被当做字符串而不被解析

const name = 'world';

hello, { name }


1
2
babel编译为

var name = 'world';
React.createElement("h1", {
title: name,
name: "{name}" //字符串
}, "hello, ", name);
1
2
3
4
5
2)花括号里边内容是js表达式,如

{ var a = 1; }


1
理论上转换后应该是React.createElement(“h1”, null, var a = 1;), 显然子元素是var a = 1;是不正确的

3)不支持if语句、for语句等等
比如

var fruits = ['apple', 'banana', 'pear'];


  • {
    if(fruits.length > 0) {
    fruits.map(item =>
  • {item}
  • )
    }

    }


1
2
3
4
5
6
7
8
9
编译不通过,理由同2,可以调整为

var fruits = ['apple', 'banana', 'pear'];


  • {
    fruits.length > 0 && fruits.map(item =>
  • {item}
  • )
    }

1
2
3
4
5
6
babel编译为:

var fruits = ['apple', 'banana', 'pear'];
React.createElement("ul", null, fruits.length > 0 && fruits.map(function (item) {
return React.createElement("li", null, item);
}));

相关文章
|
6月前
|
前端开发 JavaScript 开发者
React:JSX语法入门
React:JSX语法入门
87 0
|
12天前
|
XML 前端开发 JavaScript
react之了解jsx
react之了解jsx
|
2月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
68 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
3月前
|
XML JavaScript 前端开发
React Jsx语法入门
【8月更文挑战第13天】React Jsx语法入门
44 4
|
12天前
|
JavaScript 前端开发 容器
React零基础入门02--JSX语法基础
React零基础入门02--JSX语法基础
React零基础入门02--JSX语法基础
|
28天前
|
XML 前端开发 JavaScript
React JSX
React 使用 JSX(一种类似 XML 的 JavaScript 语法扩展)来替代传统 JavaScript 编写 UI。JSX 使代码更简洁、执行更快且类型安全。例如,`&lt;h1&gt;Hello, world!&lt;/h1&gt;` 实际上是创建一个 React 元素,通过 `ReactDOM.render()` 渲染到 DOM。注意,JSX 中使用 `className` 替代 HTML 的 `class` 属性。
|
1月前
|
XML 前端开发 JavaScript
React JSX
10月更文挑战第7天
16 2
|
3月前
|
前端开发 JavaScript
React Server Component 使用问题之添加jsx的组件化能力,如何操作
React Server Component 使用问题之添加jsx的组件化能力,如何操作
|
3月前
|
前端开发 JavaScript 开发者
React组件与JSX之间的区别是什么
【8月更文挑战第9天】 React组件与JSX之间的区别是什么
63 4
|
3月前
|
XML 前端开发 JavaScript
【React新手必看】JSX,让你的代码舞动起来!
【8月更文挑战第24天】JSX(JavaScript XML)是React开发的核心,作为一种JavaScript语法扩展,它支持在代码中使用类似HTML的标签。JSX并非模板语言,在编译过程中转换为纯JavaScript,使React能高效更新DOM并渲染页面。其优势在于直观性、简洁性和灵活性:简化复杂用户界面构建、减少代码量,并支持HTML标签、React组件及JavaScript变量的表达式插入。基本用法涉及定义组件UI,如创建包含标题和段落的简单组件。
85 0