深入了解JSX

最近开始学着使用React写东西。在写代码时会使用JSX,不了解JSX的相关知识写起代码的效率还是蛮低的。为了能更好的进入状态,打算先把JSX相关的知识和细节了解一下。在这篇文章中我们主要就是来学习一下JSX的相关知识。希望对于像我这样的初学React(或初次接触JSX)的同学有所帮助。

什么是JSX

JSX是JavaScript中的一种语法扩展。是React组件编写UI逻辑的语言扩展(JSX除了能在React中使用之外还可以用于别的地方)。虽然在不使用JSX的情况之下,React可以完全正常工作,但它是一种理想的组件处理技术,所以React从JSX中获益良多。

首先,你可能认为使用JSX就是将HTML和JavaScript混合在一起,事实并非如此,因为在使用JSX语法时,描述这个UI不是用字符串,使用的是JavaScript。这让我们能做很多事。

将JavaScript和标记(Markup)放在同一位置被认为是一种坏习惯,但事实证明,将视图与功能结合起来可以直接对视图进行推理。

为了了解这意味着什么,假设我们有一个React组件,它只渲染一个

标签。JSX允许我们以种非常类似HTML的方式声明这个元素:

class HelloWorld extends React.Component {
    render() {
        return (
            

Hello World

) } }

HelloWorld 组件中的 render() 函数看起来返回的是HTML,但实际上这是JSX。JSX在运行时被转换为常规则JavaScript。翻译后的组件看起来是这样的:

class HelloWorld extends React.Component {
    render() {
        return (
            React.createElement(
                'h1',
                {className: 'title'},
                'Hello World'
            )
        )
    }
}

虽然JSX看起来像HTML,但实际上它只是一种编写 React.createElement() 声明的更简洁的方法。当组件渲染时,它输出一个React元素树或该组件输出的HTML元素的虚拟DOM。然后,React将根据这个React元素表示确定对实际DOM进行哪些更改。 HelloWorld 组件渲染出来的React的HTML看起来像下面这样:

Hello World

为什么使用JSX

React认为渲染逻辑本质上与其他UI逻辑内在耦合,比如,在UI中需要绑定处理事件、在某些时刻状态发生变化时需要通知到UI,以及需要在UI中展示准备好的数据。

React并没有采用将标记与逻辑进行分离到不同文件这种人为地分离方式,而是通过将二者共同存放在称之为组件的松散耦合单元之中,来实现“ 关注点分离 ”。React不强制要求使用JSX,但大多数人发现,在JavaScript代码中将JSX和UI放在一起时,会在视觉上有辅助作用。它还可以使React显示更多有用的错误和警告消息。

JSX原理

在继续往下阅读之前,先稍微的了解一些有关于JSX原理相关的内容。即 JSX是经过怎么样的转化变成页面的元素的

比如说下面这个最简单的DOM元素为例,怎么使用JavaScript的对象来表现一个DOM元素的结果:

Hello World!

每个DOM元素的结构都可以用JavaScript的对象来表示。你会发现,DOM元素包含的信息其实只有三个: 标签名(HTML元素)属性(HTML元素属性)子元素(HTML元素或文本节点) 。那么上面的这个HTML标签对应的所有信息可以用下面这个对象来描述:

{
    tag: 'div',
    attrs: {className: 'title'},
    children: 'Hello World!'
}

你会发现,HTML的信息和JavaScript所包含的结构和信息其实是一样的,我们可以用JavaScript对象来描述所有能用HTML表示的UI信息。只不过写起来有点麻烦,结构看起来不太清晰。

在React中会把类似HTML的JSX结构转换成JavaScript的对象结构。比如:

Hello World!

JSX转换成JavaScript就会像下面这样:

React.createElement(
    "h1", 
    {
        class: "title"
    }, 
    "Hello World!"
);

React.createElement() 会构建一个JavaScript对象来描述HTML结构的信息,包括 标签名属性子元素 等。上面是一个较为简单的示例,对于复杂的示例,我们可以借助在线的工具来帮助我们,比如 Babel

所谓的JSX其实就是JavaScript对象。

有了之个表示HTML结构和信息的对象以后,就可以去构造真正的DOM元素,然后使用 ReactDOM.render() 函数把构造好的DOM元素塞到页面中:

ReactDOM.render(
    ,
    document.getElementById('root')
)

ReactDOM.render() 函数就是把组件渲染并且构造DOM树,然后插入到页面上某个特定的元素上 div#root

简单地总结一下,JSX经过Babel编译和React构造器转换成了JavaScript对象,然后通过 ReactDOM.render() 函数转换成DOM元素,再插入到页面中渲染出来:

接下来简单看看JSX的具体使用。

JSX的使用

在JSX中可以像下面这样定义一个包含字符串的

标签:

const element = 

Hello World!~

看起来有点像是JavaScript和HTML的结合特,但实际上它就是JavaScript。在JSX中是用来定义组件及其在标记中的位置的语法糖。事实 element 是一个 Object

JSX中嵌入表达式

在下面的示例中,声明了一个名为 eleId 的变量,然后在JSX中使用它,并且放置在 {} 中,比如:

const eleId = 'title'
const element = 

Hello World!

在JSX语法中,可以在大括号内放置任何有效的JavaScript表达式,比如:

const Hello = (user) => {
    return `Hello ${user}`;
}

const element = 

{ Hello('W3cplus.com') }

在属性中嵌入JavaScript表达式时,不要在大括号外面加上引号。应该仅使用引用( 字符串 )或大括号( 表达式 )中的一个,对于同一个属性不能同时使用这两种符号。

JSX中的特定属性

在HTML中标签中有时候会用到带有 - 中折号的属性,比如 aira-hidden ,也会有多个词组合在一起的属性,比如 tabindex 。类似这些属性在JSX中需要使用驼峰写法,比如 airaHiddentabIndex

const eleId='title'
const element = 

Hello World!

需要特别注意的是,在JSX中要是会用到 classfor 属性时,需要将 class 换成 className for 换成 htmlFor 。那是因为 classfor 是JavaScript中的关键字。

const element = (
    
)

在JSX中有些写法和HTML类似,比如一个标签里没有内容,可以使用 /> 来闭合标签,比如上面的 元素:


如果在 ReactDOM.render() 包含多个子元素时(JSX标签里包含多个子元素),需要用一个标签元素括起来,比如上面示例中的

。另外将其放置在一个括号 () 中。这是因为 render() 函数只能返回一个节点,所以如果你想返回两个兄弟节点,就需要添加一个父节点,如上面示例所示。

JSX是一个对象

浏览器不能直接执行包含JSX代码的JavaScript文件。它们必须首先转换成普通的JavaScript。需要一个叫做 转置 的过程。当然,在React中JSX是可选的,因为对于每个JSX代码都有对应的纯JavaScript代码替代,这就是 JSX的换位符

正如文章开头所提到的,Babel会把JSX转译成一个名为 React.createElement() 函数调用。比如下面的两段代码,起到的作用是同等的:

// JSX代码
ReactDOM.render(
    

title

paragraph

, document.getElementById('root') ) // JavaScript ReactDOM.render( React.createElement( "div", {id: "box"}, React.createElement( "h1", null, "title" ), React.createElement( "p", null, "paragraph" ) ), document.getElementById('root') );

JSX和JavaScript两段代码同样可以借助在线的Babel工具查看。

上面的示例也再次验证了,虽然JSX和JavaScript所起的效果是等效的,但相比而言,JavaScript要比JSX复杂的多。另外 React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:

// 简化后的结构
React.createElement(
    "h1",
    null, 
    "title"
)

实际上,JSX仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。如下JSX代码:

// JSX代码

    Click Me

编译成JavaScript代码如下:

// JavaScript代码
React.createElement(
    MyButton, 
    {
        color: "blue",
        shadowSize: 2
    }, 
    "Click Me"
);

JSX中的JS

JSX接受任何混合的JavaScript。当你需要添加一些JavaScript时,只需要将它放到大括号 {} 中(前面也有简单的提到过)。例如,下面的示例就是向你展示了在JSX中怎么引用其他地方声明的常量:

const n