一、基础
1、JSX是什么
JSX是一种像下面这样的语法:

const element = <h1>Hello, world!</h1>;
1
它是一种JavaScript语法扩展,在React中可以方便地用来描述UI。

本质上,JSX为我们提供了创建React元素方法(React.createElement(component, props, ...children))的语法糖(syntactic sugar)。上面的代码实质上等价于:

var element = React.createElement(
"h1",
null,
"Hello, world!"
);

2、JSX代表JS对象
JSX本身也是一个表达式,在编译后,JSX表达式会变成普通的JavaScript对象。

你可以在if语句或for循环中使用JSX,你可以将它赋值给变量,你可以将它作为参数接收,你也可以在函数中返回JSX。

例如下面的代码:

function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;

上面的代码在if语句中使用JSX,并将JSX作为函数返回值。实际上,这些JSX经过编译后都会变成JavaScript对象。

经过babel会变成下面的js代码:

function test(user) {
if (user) {
return React.createElement(
"h1",
null,
"Hello, ",
formatStr(user),
"!"
);
}
return React.createElement(
"h1",
null,
"Hello, Stranger."
);
}

3、在JSX中使用JavaScript表达式
在JSX中插入JavaScript表达式十分简单,直接在JSX中将JS表达式用大括号括起来即可。例如:

function formatName(user) {
return user.firstName + ' ' + user.lastName;
}

const user = {
firstName: 'Harper',
lastName: 'Perez'
};

const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);

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

上面的代码中用到了函数调用表达式fromatName(user)。

在JavaScript中,表达式就是一个短语,Javascript解释器会将其计算出一个结果,常量就是最简单的一类表达式。常用的表达式有:

变量名;
函数定义表达式;
属性访问表达式;
函数调用表达式;
算数表达式;
关系表达式;
逻辑表达式;
需要注意的是,if语句以及for循环不是JavaScript表达式,不能直接作为表达式写在{}中,但可以先将其赋值给一个变量(变量是一个JavaScript表达式):

function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} is an {description} number</div>;
}

4、JSX属性值
你可以使用引号将字符串字面量指定为属性值:

const element = <div tabIndex="0"></div>;

注意这里的”0”是一个字符串字面量。

或者你可以将一个JavaScript表达式嵌在一个大括号中作为属性值:

const element = <img src={user.avatarUrl}></img>;

这里用到的是JavaScript属性访问表达式,上面的代码将编译为:

const element = React.createElement("img", { src: user.avatarUrl });

5、JSX的Children
首先JSX可以是一个不包含Children的empty tag。如:

const element = <img src={user.avatarUrl} />;
1
JSX也可以像HTML标签一样包含Children:

const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);

这种写法在生成React元素的时候给我们带来了很大的便利,而且能够更加直观地描述UI。不然我们需要像下面这样创建和上面代码等价的React元素:

const element = React.createElement(
"div",
null,
React.createElement(
"h1",
null,
"Hello!"
),
React.createElement(
"h2",
null,
"Good to see you here."
)
);

tip: React DOM结点使用骆驼拼写法给属性命名

例如:class在JSX中应写作className,tabindex应写作tabIndex。

另外关于JSX的children需要注意的是:

React自定义组件的chilren是不会像固有的HTML标签的子元素那样自动render的,我们看下面的例子:

代码1
class Test extends React.Component {
render() {
return (
<div>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
)
}
};
ReactDOM.render(
<Test />,
document.getElementById('test')
);

以上代码定义的组件中都是build-in组件,类似div、p、ul、li等。它们中的子元素会直接render出来,像下面这样:

但是如果你使用用户定义组件,比如:

class Test extends React.Component {
render() {
return (
<Em>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</Em>
)
}
};

class Em extends React.Component {
render() {
return (<div></div>);
}
}

ReactDOM.render(
<Test />,
document.getElementById('test')
);

并不能得到跟上面代码1一样的结果,我们得到的只是一个空的div标签:

如果你想得到和代码1一样的结果,需要显示地指定props.children,像下面这样:

class Test extends React.Component {
render() {
return (
<Em>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</Em>
)
}
};

class Em extends React.Component {
render() {
return (<div>{this.props.children}</div>);
}
}

ReactDOM.render(
<Test />,
document.getElementById('test')
);

得到下面的结果:

6、JSX可自动防范注入攻击
在JSX中嵌入接收到的内容是安全的,比如:

const danger = response.potentialDanger;

cosnt ele = <h1>{title}</h1>

在默认情况下,React DOM会将所有嵌入JSX的值进行编码。这样可以有效避免xss攻击。

我们将以下代码编译后引入html:

class Test extends React.Component {
render() {
let v = "<script><\/script>";
return (
<div>
<h1>{v}</h1>
</div>
)
}
};

ReactDOM.render(
<Test />,
document.getElementById('test')
);

得到结果是:

可以看到通过JSX插入的文本自动进行了HTML转义,所以这里插入的是一段文本,而不是<script>标签。这有点类似于Js中的document.createTextNode("...")(实际上我们可以利用document.createTextNode进行HTML转义)。

作为对比,换作使用DOM元素的innerHTML属性:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="test"></div>
<script type="text/javascript">
document.getElementById("test").innerHTML="<h1><script><\/script><\/h1>";
</script>
</body>
</html>

得到结果如下:

注意文本的颜色,此时插入的是一个<script>标签。

如果你很清楚自己在做什么,希望直接将字符串不经转义编码直接插入到HTML文档流,可以使用dangerouslySetInnerHTML属性,这是一个React版的innerHTML,该属性接收一个key为__html的对象,修改代码如下:

class Test extends React.Component {
render() {
let v = {
__html: "<script><\/script>"
};
return (
<div>
<h1 dangerouslySetInnerHTML={v}/>
</div>
)
}
};

这次我们插入了<script>标签:

二、进阶
1、JSX中的props
指定JSX中的props有以下几种方式:

(1)使用JavaScript表达式
任何有效的JavaScript表达式都可以作为prop的值,使用的时候将该表达式放在一对大括号中即可:

<MyComponent foo={1 + 2 + 3 + 4} />

<YourComponent clickTodo={(id) => this.props.handleClick(id)} />

(2)使用字符串字面量
字符串字面量可以作为prop值,下面的代码是等价的:

<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

(3)使用扩展运算符
如果你想将一个prop对象传入JSX,你可以使用扩展运算符...直接将整个prop对象传入。下面的2个组件是等价的:

function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}

扩展运算符是一个es6特性。是一种传递属性的十分便利的方式。但请注意不要滥用该运算符,注意不要将一大堆毫不相关的prop一股脑全部传入下面的组件中。

2、JSX中的Children
React组件中有一个特殊的prop–props.children。它指代了JSX表达式中开闭标签中包含的内容。

下面讨论的是几种指定JSX的children的方法:

(1)使用字符串字面量
你可以直接在JSX的开闭标签中写入字符串字面量,组件得到的props.children就是该字符串值。

以下面的代码为例:

<MyComponent>Hello world!</MyComponent>
1
MyComponent的props.chilren将获得”Hello World!”字符串。通过该方式传入的字符串是未经HTML转义的。实际上你只需要像在HTML标签中写入文本那样就可以了。例如你想在一对<p>标签中写入文本”<script></script>”,HTML和JSX写法是一样的,就像下面这样:

<p><script></script></p>
1
另外需要注意的是:

JXS会自动删除一行中开头和结尾处的空白符;JSX会自动删除空行;JSX会删除紧邻标签的换行;JSX会删除字符串中的换行;字符串中的换行会被转换成一个空格。

举例来说,下面的JSX代码都是等价的:

<div>Hello World</div>

<div> Hello World </div>

<div>
Hello World
</div>

<div>
Hello
World
</div>

<div>

Hello World
</div>

(2)JSX元素作为children
我们同样可以使用JSX元素作为JSX的children,由此生成嵌套组件:

<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>

我们也可以混合使用字符串字面量和JSX作为children:

<El>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</El>

El的props.children将得到一个数组:

可以看到数组的第一个元素就是字符串“Here is a list:”,第二个元素是一个对象(JSX代表JavaScript对象)。

(3)JavaScript表达式
和prop一样,你也可以将任何有效的JavaScript表达式作为children传入,将它放在{}中就可以了。像下面这样:

<MyComponent>{'foo'}</MyComponent>

这里传入了一个常量表达式。

下面使用一个函数调用表达式来生成一个list作为children:

function Item(props) {
return <li>{props.message}</li>;
}

function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}

当然你也可以在一个字符串children中插入一个JavaScript表达式来生成一个“模板”:

function Hello(props) {
return <div>Hello {props.username}!</div>;
}

(4)函数children
首先说明,这不是一种常见的用法。

实际上,传入自定义组件的children并没有严格的限制,只要在React需要render的时候能将它们转换成可以render的东西就行了。

下面是一个函数children的例子:

function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}

// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}

实际上,我们更通常的情况下是将(index) => <div key={index}>This is item {index} in the list</div>作为一个prop传入子组件。这个例子只是作为一种理解上的扩展。

(5)有关布尔值、Null以及Undefined
布尔值,Null以及Undefined可以作为有效的children,但他们不会被render,下面的JSX表达式都会render一个空的div标签:

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{true}</div>

关于此有一个有趣的应用,在条件render中,下面的<Header />只有在show为true时才会render:

<div>
{showHeader && <Header />}
<Content />

3、注意事项
(1)使用JSX时要引入React库
前面已经解释过了,JSX是React.createElement方法的语法糖,因此在使用JSX的作用域中必须引入React库。

如果你使用了JS打包工具,你可以在文件的头部作如下引用:

import React from 'react';
1
或者你不使用打包工具,也可以直接通过script标签引入React,比如:

//本地
<script src="./react.js"></script>

//或者BootCDN
<script src="http://cdn.bootcss.com/react/15.4.0/react.js"></script>

此时React将作为一个全局变量被引入,变量名就是’React’。

(2)注意引入JSX中用到的自定义组件
JSX中用到的组件可能并不会在JavaScript中直接引用到,但自定义组件本质上就是一个JS对象,你在JSX中使用的时候,需要首先将该组件引入到当前作用域:

import MyComponent from './MyComponent.js'

...

<Outer>
<MyComponent />
</Outer>

(3)自定义组件首字母一定要大写
JSX中小写字母开头的element代表HTML固有组件如div,span,p,ul等。用户自定义组件首字母一定要大写如<Header>、<Picker>。

(4)元素标签名不能使用表达式
下面的代码将产生错误:

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// Wrong! JSX标签名不能使用表达式
return <components[props.storyType] story={props.story} />;
}

如果你需要使用一个表达式来决定元素标签,你应该先将该表达式的值赋给一个大写字母开头的变量:

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}

(5)设置style属性
在设置标签style属性的时候,要注意,我们是将一个描述style的对象以JavaScipt表达式的形式传入。因此应该有2层大括号:

<div style={{color:'red', margin:'10px auto'}}></div>

JSX语法详解的更多相关文章

  1. Velocity魔法堂系列二:VTL语法详解

    一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...

  2. Hive笔记--sql语法详解及JavaAPI

    Hive SQL 语法详解:http://blog.csdn.net/hguisu/article/details/7256833Hive SQL 学习笔记(常用):http://blog.sina. ...

  3. Hadoop Hive sql语法详解

    Hadoop Hive sql语法详解 Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构 化的数据文件 ...

  4. Thymeleaf3语法详解和实战

    Thymeleaf3语法详解 Thymeleaf是Spring boot推荐使用的模版引擎,除此之外常见的还有Freemarker和Jsp.Jsp应该是我们最早接触的模版引擎.而Freemarker工 ...

  5. Xpath语法详解

    1.简介 XPath是一门在XML和HTML文档中查找信息的语言,可以用来在XML和HTML文档中对元素和属性进行遍历 XPath的安装 Chrome插件XPath Helper 点Chrome浏览器 ...

  6. mysql用户授权、数据库权限管理、sql语法详解

    mysql用户授权.数据库权限管理.sql语法详解 —— NiceCui 某个数据库所有的权限 ALL 后面+ PRIVILEGES SQL 某个数据库 特定的权限SQL mysql 授权语法 SQL ...

  7. Java8的Stream语法详解(转载)

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

  8. [持续交付实践] pipeline使用:语法详解

    一.引言 jenkins pipeline语法的发展如此之快用日新月异来形容也不为过,而目前国内对jenkins pipeline关注的人还非常少,相关的文章更是稀少,唯一看到w3c有篇相关的估计是直 ...

  9. Java 8系列之Stream的基本语法详解

    本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...

随机推荐

  1. Android学习之CoordinatorLayout+AppBarLayout

    •AppBarLayout 简介 AppbarLayout 是一种支持响应滚动手势的 app bar 布局: 基本使用 新建一个项目,命名为 TestAppBarLayout: 修改 activity ...

  2. python mac地址计算

    思路是10/16进制的转换和字符串的处理 开始造轮子 1.判断是否是mac地址 正则匹配是否符合条件 1 import re 2 3 def isMac(string): 4 preg = re.co ...

  3. 以绝对优势立足:从CDN和云存储来聊聊云生态的崛起

    以绝对优势立足:从CDN和云存储来聊聊云生态的崛起 前面几期文章我们介绍了混合云模式,以及面向应用层的云架构解决方案的Spring Cloud.接下来,我们就以蘑菇街的两个具体案例,来分享一下基于混合 ...

  4. MySQL巩固学习记录(一)

    mysql下载安装 一.采用图形化界面安装 (初期只安装server服务端就可以了,别的不多赘述) 二.采用压缩版安装 1.将文件解压缩到自己想要的路径 2. 添加环境变量,即mysql的bin目录 ...

  5. 生产环境中mysql数据库由主从关系切换为主主关系

    目录 一.清除原从数据库数据及主从关系 1.1.关闭主从数据库原有的主从关系 1.2.清除从数据库原有数据 二.将主库上的数据备份到从库 2.1.备份主库数据到从库 2.2.在从库使用tsc.sql文 ...

  6. 数据库MySQL六

    介绍什么是JDBC JAVA SE也有 提高综合篇 JDBC(Java Database Connectivity) :java和数据库的连接技术,sun公司推出的一套java应用程序访问数据库的技术 ...

  7. 死磕Spring之AOP篇 - Spring AOP自动代理(一)入口

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  8. Linux下禁用笔记本触摸板

    1 概述 在Linux下禁用触摸板的方法有很多,这里列举三种: 图形界面配置关闭 modprobe关闭 xinput关闭 2 图形界面配置关闭 笔者的环境为Manjaro+Xfce,其他的桌面也应该类 ...

  9. (十六)VMware Harbor 复制镜像

    Step 1: 点击复制镜像后 Step 2: 填写复制镜像信息 Step 3:在sx628下,会多出一个镜像

  10. The Blocks Problem UVA - 101

      Many areas of Computer Science use simple, abstract domains for both analytical and empirical stud ...