JSX语法详解
一、基础
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语法详解的更多相关文章
- Velocity魔法堂系列二:VTL语法详解
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
- Hive笔记--sql语法详解及JavaAPI
Hive SQL 语法详解:http://blog.csdn.net/hguisu/article/details/7256833Hive SQL 学习笔记(常用):http://blog.sina. ...
- Hadoop Hive sql语法详解
Hadoop Hive sql语法详解 Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构 化的数据文件 ...
- Thymeleaf3语法详解和实战
Thymeleaf3语法详解 Thymeleaf是Spring boot推荐使用的模版引擎,除此之外常见的还有Freemarker和Jsp.Jsp应该是我们最早接触的模版引擎.而Freemarker工 ...
- Xpath语法详解
1.简介 XPath是一门在XML和HTML文档中查找信息的语言,可以用来在XML和HTML文档中对元素和属性进行遍历 XPath的安装 Chrome插件XPath Helper 点Chrome浏览器 ...
- mysql用户授权、数据库权限管理、sql语法详解
mysql用户授权.数据库权限管理.sql语法详解 —— NiceCui 某个数据库所有的权限 ALL 后面+ PRIVILEGES SQL 某个数据库 特定的权限SQL mysql 授权语法 SQL ...
- Java8的Stream语法详解(转载)
1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...
- [持续交付实践] pipeline使用:语法详解
一.引言 jenkins pipeline语法的发展如此之快用日新月异来形容也不为过,而目前国内对jenkins pipeline关注的人还非常少,相关的文章更是稀少,唯一看到w3c有篇相关的估计是直 ...
- Java 8系列之Stream的基本语法详解
本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...
随机推荐
- java例题_43 求0—7所能组成的奇数个数
1 /*43 [程序 43 求奇数个数] 2 题目:求 0-7 所能组成的奇数个数. 3 */ 4 5 /*分析 6 * 1.0不能作最高位且最低位只能是1,3,5,7; 7 * 2.没有限定是几位数 ...
- 自动获取IMC系统所有网络设备资产信息
1 #coding=utf8 2 3 """ 4 CMDB接口调用 5 """ 6 import csv 7 import json 8 i ...
- BUUCTF 基础CODE REVIEW
1.说明: 题目来自于BUUCTF 的基础部分,内容就如题,是一个代码审计.代码如下: <?php /** * Created by PhpStorm. * User: jinzhao * Da ...
- 基于SageMath的数学网站——本科毕业开发项目
1 绪论 1.1研究背景 我国是一个拥有15亿人口的大国.其中,据2017年的统计,全国共有大学生2600万左右.如此数量众多的大学生,都会有着学习基础数理课程的需求.而在高校的数学教学中,教授最多最 ...
- NameError: name 'foo' is not defined Python常见错误
1.变量或者函数名拼写错误 2.在一个定义新变量中使用增值操作符 没有定义的变量被引用时候会出现此错误
- [BFS]A. 【例题1】走迷宫
A . [ 例 题 1 ] 走 迷 宫 解析 简单的BFS模板题 Code #include <bits/stdc++.h> #define N 1005 using namespace ...
- [模拟]P1047 校门外的树
校门外的树 题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,- ...
- WPF -- 使用当前进程打开自定义文件的一种方式
问题描述 当双击打开自定义格式的文件时,希望使用当前正在运行的进程,而不是另起一个进程. 本文介绍一种方式解决如上问题,方案参考user3582780的解答 设置自定义文件格式的默认打开方式 参考链接 ...
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
目录 1,前言 2,安装nvm 3,nvm的使用 4,错误处理 5,修改npm默认镜像源 6,win10下cnpm报错 1,前言 注意:此教程仅限Windows,Mac可能不适用 在我们的日常开发中可 ...
- HUAWEI防火墙双出口根据链路优先级主备备份
组网图形 组网需求 通过配置根据链路优先级主备备份,FW可以在主接口链路故障时,使用备份接口链路转发流量,提高传输的可靠性. 如图1所示,企业从ISP1租用2条链路,带宽均为50M,从ISP2租用1条 ...