文档章节

React学习(4)——深入说明JSX与props

随风溜达的向日葵
 随风溜达的向日葵
发布于 2017/04/06 10:56
字数 2712
阅读 161
收藏 5

JSX说明

我们可以将JSX理解为React.createElement(component, props, ...children)方法的语法糖。JSX的代码:

<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

最终会被编译成一个React Element 对象:

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

我们可以使用“闭标签”来表示没有子元素的情况:

<div className="sidebar" />

它会编译成:

React.createElement(
  'div',
  {className: 'sidebar'},
  null
)

如果你想尝试各种JSX是如何转换成JavaScript代码的,你可以打开这个网站试试:the online Babel compiler

React组件的作用域

JSX标签声明的第一个部分是React元素的类型(Type)。首字母大写表明这个JSX标签是一个React的组件。这些标签会被编译成对命名变量的直接引用,因此如果你使用JSX的<Foo />表达式,那么Foo方法或对象必须包含在当前域中(可以理解在当前页面或闭包中可以找到这个对象)。

import React from 'react';
import Foo from './Foo'; //ES6的import语法,必须现在闭包中引入才能使用

React的作用域

因为JSX需要调用React.createElement来进行编译,因此在使用JSX表达式时,React应该始终被引用到当前域中(可以理解为页面或闭包可以访问到React.createElement)。

如下面代码的例子,即使没有显示的使用React.createElement方法,但是在使用任何React组建时,React和组件都必须在使用时被引入:

import React from 'react';
import CustomButton from './CustomButton';

function WarningButton() {
  // return React.createElement(CustomButton, {color: 'red'}, null);
  return <CustomButton color="red" />;
}

利用点号“.”来引用组件

在JSX语法中,可以使用点号来引入React组件。这样做的好处是如果某一个模块很多种React组件,我们可以很方便的将其归类。例如 MyComponents.DatePicker 是一个组件,我们可以直接使用JSX语法使用他:

import React from 'react';

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
}

用户定义的组件首字母必须大写

当一个元素以小写字母开头时它会被识别为一个内置的组件,比如<div>或<span>将会转译成字符串'div'、'span'传递给React.createElement方法,最终执行React.createElement('div')。而如果以大写字母开头,例如<Foo />,则会转译成一个对象作为参数传递,最终执行的方法是React.createElement(Foo)。

我们推荐在命名自定义组件时将首字母大写。如果不得不将自定义组件的首字母设置为小写字母,那么在使用JSX之前将其赋值给大写的变量。

下面的代码将不会按照预计执行:

import React from 'react';

// 错误!自定义组件首字母大写
function hello(props) {
  // 正确!<div>是一个HTML标签
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // 错误!因首字母没有大写,React会认为<hello>是一个HTML标签:
  return <hello toWhat="World" />;
}

我们必须修改为:

import React from 'react';

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

function HelloWorld() {
  return <Hello toWhat="World" />;
}

在运行时确定类型

由于JavaScript的语言特性,我们可以在运行时再确定类型。但是我们不能将这个常规的经验应用在JSX表达式中。不过我们可以在JSX表达式之外去确定“运行时类型”,只要将JSX表达式赋值给一个大写变量即可。例子:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

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

function Story(props) {
  // 运行时错误! JSX不支持这样的表达式.
  return <components[props.storyType] story={props.story} />;
}

调整为:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

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

function Story(props) {
  // 用一个大写变量来指向JSX声明的组件.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

这样写适用于我们基于某些条件来决定使用某个组件的场景。

使用Prop传递JSX参数

JavaScript表达式

可以传递任何JavaScript表达式作为props参数,JSX中嵌套的表达式要用{}包裹住。例如:

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

MyComponent组件最终传入的参数是props.foo = 10,因为在传入参数之前“1 + 2 + 3 + 4”这个表达式已经先完成了计算。

在JSX的{}中不能使用for等循环表达式。可以在JSX表达式之外进行循环和遍历。例如:

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>;
}

字符串文字

也可以直接使用字符串作为一个参数传递,下面的表达式是一样的效果:

//直接使用字符串
<MyComponent message="hello world" />

//在JavaScript表达式中字符串作为一个参数传入
<MyComponent message={'hello world'} />

如果直接传递一个字符串,它将会被解析成未转义的HTML语法,比如下面的2个表达式会得带一样的结果:

//传入字符串
<MyComponent message="&lt;3" />

//通过JavaScript语法传入变量
<MyComponent message={'<3'} />

Prop参数默认为"True"

如果传递了没有数据的prop参数,它的值默认为true。因此一下2个表达式完全一样:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

通常情况下不推荐像上面这样使用,因为这会和ES6的简写语法混淆——{foo}是{foo:foo}的简写而不是{foo:true}。提供这个特性仅仅是因为很像HTML语法。

属性扩展传递(Spread 特性)

如果已经有一个类型为object的props,并且想将这个props传递给JSX。可以使用ES6的“...”语法来扩展传递整个参数。下面的表达式是一样的效果:

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

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

属性扩展传递是一个非常有用的特性,尤其是当参数可变时。然而这个特性也会使得代码混乱并且传递一些无关紧要的参数到组件中,建议谨慎使用这个特性。

JSX中的子标签

JSX表达式既可以使用开放型标签页也可以使用封闭型标签(例如 开放型标签:<div></div>。封闭型标签:<img />)。开放型标签中的内容会通过props.children传递到组件中。

传递字符串

可以在开放标签之间传递一个字符串,然后在组件中通过props.children获取的数据就是一个字符串。这对于许多内置的HTML标签很有用。例如:

<MyComponent>Hello world!</MyComponent>

在组件“MyComponent”中通过props.children可以获取到"Hello world!"字符串。你只需要按照需求编写字符串而不必考虑HTML的为转移特性, 因此你们这样写JSX来影响HTML代码:

<div>This is valid HTML &amp; JSX at the same time.</div>

JSX会移除掉开头和结尾的的空白字符、空白行、删除与标签相邻的新行。会将文字中间的换行、整行空白符号转义为一个空格符。基于这个特性,下面的表达式结果都是一样的:

//标准
<div>Hello World</div>

//前后换行
<div>
  Hello World
</div>

//前后换行,中间换行
<div>
  Hello
  World
</div>

//前空白行,前换行。
<div>

  Hello World
</div>

JSX的子元素

在JSX的开放标签中间,你可以设置多个子标签,这些标签的内容都可以通过props.children获取:

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

也可以同时使用多种类型的子元素,这一点JSX和HTML几乎一模一样,我们可以把JSX的解析过程看成一个HTML,例如:

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

一个React组件不能一次返回多个React元素,但是一条独立的JSX表达式可以包含多个子元素,因此,我们可以使用一个外层标签来包裹子元素实现一个React组件渲染多个节点。

JavaScript表达式作为子元素

在JSX的子元素中,你也可以使用JavaScript表达式,JSX使用{}来表示要执行一段JavaScript语句。例如下面的2个表达式执行完毕后是一样的效果:

<MyComponent>foo</MyComponent>

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

在开发过程中,我们经常会遇到需要渲染一个JSX表达式列表的情况,我们可以直接将迭代语句嵌入到子元素中去处理,例如:

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>
  );
}

JavaScript表达式可以和任意类型的子元素混合使用,例如我们将其作为一个模板工具使用:

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

Function作为子元素

通常情况下,将JavaScript表达式嵌入到JSX中将会被成一段字符串、一个React元素或者一个包含字符串和React元素的列表。然而,props.chilidren和其他props参数一样,它可以传递任何类型的数据而不仅仅是React知晓的类型。例如,自定义自建Repeat,子元素将接收到一个方法列表,在Repeat逐一执行每个方法:

// prop.children会接收一个方法列表,每个方法将会被逐一调用。
function Repeat(props) {
  let items = [];
  for (let i = 0; i < props.numTimes; i++) {
    items.push(props.children(i));
  }
  return <div>{items}</div>;
}

// numTimes传递的是循环的次数,而子元素则是一系列方法。会在Repeat组件中被执行。
function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}

props.children可以传递任意参数给自定义的组件,只要在React发生渲染之前处理成React可以理解的表达式即可,这样可以极大的延伸JSX的灵活性。

Booleans, Null, and Undefined被忽略

falsenullundefined, and true 都是有效的元素,它们在表达式中的含义为“不需要渲染”。下面的表达式都会得到同样的结果:   

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

这样的特性有利于编写各种条件表达式。例如下面的例子,只有当showHeadertrue时才会渲染<Header />元素:

 

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

需要特别说明的是falsy值(参看mozilla官文说明),当变量值为数字型的0时,React还是会将其渲染的。下面的代码当 props.messages.length结果为0时,依然会发生渲染:

<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>

需要始终保持&&之前的表达式结果都是boolean类型,所以为了得到正确的结果,我们需要将表达式调整为:

<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>

最后,如果想要将 falsetruenull, or undefined 这些输出到组件中,需要将他们转换成字符串(说明):

<div>
  My JavaScript variable is {String(myVariable)}.
</div>

原文地址:https://www.chkui.com/article/react/react_understand_jsx_and_props

© 著作权归作者所有

共有 人打赏支持
随风溜达的向日葵
粉丝 281
博文 81
码字总数 174719
作品 0
广州
其他
私信 提问
React 深入说明JSX语法与Props特性

JSX说明 我们可以将JSX理解为React.createElement(component, props, ...children)方法的语法糖。JSX的代码: 最终会被编译成一个React Element 对象: 我们可以使用“闭标签”来表示没有子元...

溜达向日葵
2018/07/18
0
0
[边学边练]用简单实例学习React

每一个的地方,每一种的知识,每一种事物,都是从陌生到熟悉。在这个过程里面,或许能开阔眼界,增长见识,体验乐趣。一切都归于我们的心态与行动。 1.前言 学习之路不可停止,最近在玩 Reac...

守候i
2018/08/27
0
0
从 0 到 1 实现 React 系列 —— 组件和 state|props

组件即函数 在上一篇 JSX 和 Virtual DOM 中,解释了 JSX 渲染到界面的过程并实现了相应代码,代码调用如下所示: 本小节,我们接着探究组件渲染到界面的过程。在此我们引入组件的概念,,如...

牧云云
2018/07/12
0
0
React 内部机制探秘 - React Component 和 Element(文末附彩蛋demo和源码)

React 内部机制探秘 - React Component 和 Element(文末附彩蛋demo和源码) 这篇文章比较偏基础,但是对入门 React 内部机制和实现原理却至关重要。算是为以后深入解读的一个入门,如果您已...

lucas_580e331d326b4
2017/09/29
0
0
React 教程:快速上手指南

翻译:疯狂的技术宅 原文:www.toptal.com/react/react… 前端和 JavaScript 是一个奇怪的世界。大量不断推出的新技术的同时,也在被不需要它们的人嘲笑,往往很多人都会这样做。我们有时会对...

疯狂的技术宅
02/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

容器服务

简介 容器服务提供高性能可伸缩的容器应用管理服务,支持用 Docker 和 Kubernetes 进行容器化应用的生命周期管理,提供多种应用发布方式和持续交付能力并支持微服务架构。 产品架构 容器服务...

狼王黄师傅
昨天
3
0
高性能应用缓存设计方案

为什么 不管是刻意或者偶尔看其他大神或者大师在讨论高性能架构时,自己都是认真的去看缓存是怎么用呢?认认真真的看完发现缓存这一块他们说的都是一个WebApp或者服务的缓存结构或者缓存实现...

呼呼南风
昨天
12
0
寻找一种易于理解的一致性算法(扩展版)

摘要 Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可...

Tiny熊
昨天
3
0
聊聊GarbageCollectionNotificationInfo

序 本文主要研究一下GarbageCollectionNotificationInfo CompositeData java.management/javax/management/openmbean/CompositeData.java public interface CompositeData { public Co......

go4it
昨天
3
0
阿里云ECS的1M带宽理解

本文就给大家科普下阿里云ECS的固定1M带宽的含义。 “下行带宽”和“上行带宽” 为了更好的理解,需要先给大家解释个词“下行带宽”和“上行带宽”: 下行带宽:粗略的解释就是下载数据的最大...

echojson
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部