React中JSX的理解
@H_502_3@JSX是快速生成@H_502_3@react元素的一种语法,实际是@H_502_3@React.createElement(component,props,...children)的语法糖,同时@H_502_3@JSX也是@H_502_3@Js的语法扩展,包含所有@H_502_3@Js功能。
描述
JSX发展过程
在之前,@H_502_3@Facebook是@H_502_3@PHP大户,所以@H_502_3@React最开始的灵感就来自于@H_502_3@PHP。
在@H_502_3@2004年这个时候,大家都还在用@H_502_3@PHP的字符串拼接来开发网站。
$str = "<ul>";
foreach ($talks as $talk) {
$str += "<li>" . $talk->name . "</li>";
}
$str += "</ul>";
这种方式代码写出来不好看不说,还容易造成@H_502_3@XSS等安全问题。应对方法是对用户的任何输入都进行转义@H_502_3@Escape,但是如果对字符串进行多次转义,那么反转义的次数也必须是相同的,否则会无法得到原内容,如果又不小心把@H_502_3@HTML标签给转义了,那么@H_502_3@HTML标签会直接显示给用户,从而导致很差的用户体验。
到了@H_502_3@2010年,为了更加高效的编码,同时也避免转义@H_502_3@HTML标签的错误,@H_502_3@Facebook开发了@H_502_3@XHP。@H_502_3@XHP是对@H_502_3@PHP的语法拓展,它允许开发者直接在@H_502_3@PHP中使用@H_502_3@HTML标签,而不再使用字符串。
$content = <ul />;
foreach ($talks as $talk) {
$content->appendChild(<li>{$talk->name}</li>);
}
这样的话,所有@H_502_3@HTML标签都使用不同于@H_502_3@PHP的语法,我们可以轻易的分辨哪些需要转义哪些不需要转义。不久的后来,@H_502_3@Facebook的工程师又发现他们还可以创建自定义标签,而且通过组合自定义标签有助于构建大型应用。
到了@H_502_3@2013年,前端工程师@H_502_3@Jordan Walke向他的经理提出了一个大胆的想法:把@H_502_3@XHP的拓展功能迁移到@H_502_3@Js中,首要任务是需要一个拓展来让@H_502_3@Js支持@H_502_3@XML语法,该拓展称为@H_502_3@JSX。因为当时由于@H_502_3@Node.js在@H_502_3@Facebook已经有很多实践,所以很快就实现了@H_502_3@JSX。
const content = (
<TalkList>
{talks.map(talk => <Talk talk={talk} />)}
</TalkList>
);
为何使用JSX
@H_502_3@React认为渲染逻辑本质上与其他@H_502_3@UI逻辑内在耦合,比如在@H_502_3@UI中需要绑定处理事件、在某些时刻状态发生变化时需要通知到@H_502_3@UI,以及需要在@H_502_3@UI中展示准备好的数据。
@H_502_3@React并没有采用将标记与逻辑进行分离到不同文件这种人为地分离方式,而是通过将二者共同存放在称之为组件的松散耦合单元之中,来实现关注点分离。
@H_502_3@React不强制要求使用@H_502_3@JSX,但是大多数人发现,在@H_502_3@JavaScript代码中将@H_502_3@JSX和@H_502_3@UI放在一起时,会在视觉上有辅助作用,它还可以使@H_502_3@React显示更多有用的错误和警告消息。
简单来说,@H_502_3@JSX可以很好的描述页面@H_502_3@html结构,很方便的在@H_502_3@Js中写@H_502_3@html代码,并具有@H_502_3@Js的全部功能。
优点
@H_502_3@JSX的优点主要体现在以下三点:
- 快速,@H_502_3@JSX执行更快,因为它在编译为@H_502_3@JavaScript代码后进行了优化。
- 安全,与@H_502_3@JavaScript相比,@H_502_3@JSX是静态类型的,大多是类型安全的。使用@H_502_3@JSX进行开发时,应用程序的质量会变得更高,因为在编译过程中会发现许多错误,它也提供编译器级别的调试功能。
- 简单,语法简洁,上手容易。
JSX实例
规则定义
- @H_502_3@JSX只能有一个根元素,@H_502_3@JSX标签必须是闭合的,如果没有内容可以写成自闭和的形式@H_502_3@<div />。
- 可以在@H_502_3@JSX通过@H_502_3@{}嵌入@H_502_3@Js表达式。
- @H_502_3@JSX会被@H_502_3@babel转换成@H_502_3@React.createElement的函数调用,调用后会创建一个描述@H_502_3@HTML信息的@H_502_3@Js对象。
- @H_502_3@JSX中的子元素可以为字符串字面量。
- @H_502_3@JSX中的子元素可以为@H_502_3@JSX元素。
- @H_502_3@JSX中的子元素可以为存储在数组中的一组元素。
- @H_502_3@JSX中的子元素可以为@H_502_3@Js表达式,可与其他类型子元素混用;可用于展示任意长度的列表。
- @H_502_3@JSX中的子元素可以为函数及函数调用。
- @H_502_3@JSX中的子元素如果为@H_502_3@boolean/null/undefined将会被忽略,如果使用@H_502_3@&&运算符,需要确保前面的是布尔值,如果是@H_502_3@0/1则会被渲染出来。
- 在对象属性中定义@H_502_3@React组件,可以使用@H_502_3@object的点语法使用该组件。
- @H_502_3@React元素会被转换为调用@H_502_3@React.createElement函数,参数是组件,因此@H_502_3@React和该组件必须在作用域内。
- @H_502_3@React元素需要大写字母开头,或者将元素赋值给大小字母开头的变量,小写字母将被认为是@H_502_3@HTML标签。
- 不能使用表达式作为@H_502_3@React元素类型,需要先将其赋值给大写字母开头的变量,再把该变量作为组件。
JSX的使用
在示例中我们声明了一个名为@H_502_3@name的变量,然后在@H_502_3@JSX中使用它,并将它包裹在大括号中。在@H_502_3@JSX语法中,可以在大括号内放置任何有效的@H_502_3@JavaScript表达式。例如@H_502_3@2 + 2、@H_502_3@user.firstName或@H_502_3@formatName(user)都是有效的@H_502_3@JavaScript表达式。
const name = "Josh Perez";
const element = <h1>Hello,{name}</h1>;
ReactDOM.render(
element,document.getElementById("root")
);
同样@H_502_3@JSX也是一个表达式,@H_502_3@JSX天生就是需要被编译之后才可以使用的,在编译之后@H_502_3@JSX表达式会被转为普通@H_502_3@JavaScript函数调用,并且对其取值后得到@H_502_3@JavaScript对象。也就是说,你可以在@H_502_3@if语句和@H_502_3@for循环的代码块中使用@H_502_3@JSX,将@H_502_3@JSX赋值给变量,把@H_502_3@JSX当作参数传入,以及从函数中返回@H_502_3@JSX。
function getGreeting(user) {
if (user) {
return <h1>Hello,{formatName(user)}!</h1>;
}
return <h1>Hello,Stranger.</h1>;
}
通常可以通过使用引号来将属性值指定为字符串字面量,也可以使用大括号来在属性值中插入一个@H_502_3@JavaScript表达式,在属性中嵌入@H_502_3@JavaScript表达式时,不要在大括号外面加上引号。因为@H_502_3@JSX语法上更接近@H_502_3@JavaScript而不是@H_502_3@HTML,所以@H_502_3@React DOM使用@H_502_3@camelCase小驼峰命名来定义属性的名称,而不使用@H_502_3@HTML属性名称的命名约定。例如@H_502_3@JSX里的@H_502_3@class变成了@H_502_3@className,而@H_502_3@tabindex则变为@H_502_3@tabIndex。
const element1 = <div tabIndex="0"></div>;
const element2 = <img src={user.avatarUrl}></img>;
@H_502_3@JSX中也可以使用@H_502_3@</>来闭合标签,另外@H_502_3@JSX同样也可以直接定义很多子元素。
const element1 = <img src={user.avatarUrl} />;
const element2 = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
你可以安全地在@H_502_3@JSX当中插入用户输入内容,@H_502_3@React DOM在渲染所有输入内容之前,默认会进行转义,这样可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容,所有的内容在渲染之前都被转换成了字符串,可以有效地防止 @H_502_3@XSS跨站脚本攻击。
const title = response.potentiallyMalicIoUsInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;
实际上@H_502_3@Babel会把@H_502_3@JSX转译成一个名为@H_502_3@React.createElement()函数调用,通过@H_502_3@React.createElement()定义的元素与使用@H_502_3@JSX生成的元素相同,同样这就使得@H_502_3@JSX天生就是需要编译的。
const element1 = (
<h1 className="greeting">
Hello,world!
</h1>
);
// 等价
const element2 = React.createElement(
'h1',{className: 'greeting'},'Hello,world!'
);
@H_502_3@React.createElement()会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象。这些对象被称为@H_502_3@React 元素,它们描述了你希望在屏幕上看到的内容,@H_502_3@React通过读取这些对象,然后使用它们来构建@H_502_3@DOM以及保持随时更新。
// 注意:这是简化过的结构
const element = {
type: 'h1',props: {
className: 'greeting',children: 'Hello,world!'
}
};
实际上,这就是虚拟@H_502_3@DOM的一个节点,@H_502_3@Virtual DOM是一种编程概念,在这个概念里,@H_502_3@UI以一种理想化的,或者说虚拟的表现形式被保存于内存中,并通过如@H_502_3@ReactDOM等类库使之与真实的@H_502_3@DOM同步,这一过程叫做协调。这种方式赋予了@H_502_3@React声明式的@H_502_3@API,您告诉@H_502_3@React希望让@H_502_3@UI是什么状态,@H_502_3@React就确保@H_502_3@DOM匹配该状态,这样可以从属性操作、事件处理和手动@H_502_3@DOM更新这些在构建应用程序时必要的操作中解放出来。
与其将@H_502_3@Virtual DOM视为一种技术,不如说它是一种模式,人们提到它时经常是要表达不同的东西。在@H_502_3@React的世界里,术语@H_502_3@Virtual DOM通常与@H_502_3@React元素关联在一起,因为它们都是代表了用户界面的对象,而@H_502_3@React也使用一个名为@H_502_3@fibers的内部对象来存放组件树的附加信息,上述二者也被认为是@H_502_3@React中@H_502_3@Virtual DOM 实现的一部分,@H_502_3@Virtual DOM也为使用@H_502_3@diff算法奠定了基础。
<div class="root" name="root">
<p>1</p>
<div>11</div>
</div>
// 使用Js对象去描述上述节点以及文档
{
type: "tag",tagName: "div",attr: {
className: "root"
name: "root"
},parent: null,children: [{
type: "tag",tagName: "p",attr: {},parent: {} /* 父节点的引用 */,children: [{
type: "text",tagName: "text",content: "1"
}]
},{
type: "tag",content: "11"
}]
}]
}
示例
<!DOCTYPE html>
<html>
<head>
<Meta charset="UTF-8" />
<title>JSX示例</title>
</head>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timer = setInterval(() => this.tick(),1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
tick() {
this.setState({ date: new Date() });
}
render() {
return (
<div>
<h1>{this.props.tips}</h1>
<h2>Now: {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
class App extends React.Component{
constructor(props){
super(props);
this.state = {
showClock: true,tips: "Hello World!"
}
}
updateTips() {
this.setState((state,props) => ({
tips: "React update"
}));
}
changeDisplayClock() {
this.setState((state,props) => ({
showClock: !this.state.showClock
}));
}
render() {
return (
<div>
{this.state.showClock && <Clock tips={this.state.tips} />}
<button onClick={() => this.updateTips()}>更新tips</button>
<button onClick={() => this.changeDisplayClock()}>改变显隐</button>
</div>
);
}
}
var vm = ReactDOM.render(
<App />,document.getElementById("root")
);
</script>
</html>
每日一题
@H_502_3@https://github.com/WindrunnerMax/EveryDay
参考
@H_502_3@https://www.zhihu.com/question/265784392 https://juejin.cn/post/6844904127013584904 https://zh-hans.reactjs.org/docs/introducing-jsx.html