chenxiaochun / blog

🖋️ChenXiaoChun's blog
181 stars 15 forks source link

深入 JSX #57

Open chenxiaochun opened 6 years ago

chenxiaochun commented 6 years ago

实际上,你可以把 JSX 理解为都是React.createElement(component, props, ...children)方法的语法糖而已。

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

会被编译为:

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

当一个组件没有子元素的时候,你可以使用自闭合形式:

<div className="sidebar" />

会被编译为:

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

如果你想测试把一些具体的 JSX 转换成 js,可以使用这个在线的 babel 编译器

指定 React 元素类型

JSX 标签的第一部分决定了 React 元素类型。

首字母大写说明 JSX 标签引用了一个 React 组件。这些标签会被编译成直接引用它的命名变量,因此,假设你使用了<Foo />表达式,一定要确保它必须在作用域中。

React必须存在于作用域中

因为 JSX 编译器一定会调用React.createElement,所以,必须确保React存在于 JSX 代码的作用域中。

例如,虽然下面的代码中没有在 js 代码中直接调用ReactCustomButton,但是也还是需要引入它们:

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

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

如果你没有使用任何 js 打包器,而是用<script>标签加载的方式,那它也以React全局变量的形式存在作用域中了。

使用点符号

你也可以使用点符号从 JSX 中去引用一个 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>。并且它们会被以字符串的形式传递给React.createElement方法。

当一个元素以大写字母开头时,例如:<Foo />。就必须要有相应的组件定义或者在 js 文件中有相应的引入。

推荐组件都应该以大写字母开头。但是,如果你必须要以小写字母开头时,建议先把它赋值给一个大写字母开头的变量,然后再去 JSX 中引用它。

例如,下面的代码就是错误的:

import React from 'react';

// Wrong! This is a component and should have been capitalized:
function hello(props) {
  // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
  return <hello toWhat="World" />;
}

修复的办法,就是把hello重新命名为Hello并且使用<Hello />来引用它:

import React from 'react';

// Correct! This is a component and should be capitalized:
function Hello(props) {
  // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // Correct! React knows <Hello /> is a component because it's capitalized.
  return <Hello toWhat="World" />;
}

选择运行时类型

如果你想使用一个表达式来决定 React 的元素类型时,就必须把它赋值给一个大写字母开头的变量之后再去引用它。这种场景通常用于当你想基于一个 prop 渲染出不同的组件时。

错误的用法:

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

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

function Story(props) {
  // Wrong! JSX type can't be an expression.
  return <components[props.storyType] story={props.story} />;
}

修复这个问题的方法,就是把components赋值给一个大写字母开头的变量:

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

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

JSX 中的 props

可以在 JSX 中使用很多不同的方式去指定一个 prop。

js 表达式作为 prop

你可以传递任何使用{}包裹的 js 表达式作为 prop。例如:

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

对于MyComponent来说,它的props.foo的值就是10,因为表达式会被计算。

在 js 中,if语句和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>;
}

想了解更多关于条件渲染循环的知识,可以去相应的章节。

字符串

可以传递一个普通字符串给 prop。下面两种 JSX 表达式都是等价的:

<MyComponent message="hello world" />

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

下面传递给 prop 的字符串会被解析为 html 非转义的形式,因此下面的两种也是等价的:

<MyComponent message="&lt;3" />

<MyComponent message={'<3'} />

这种使用方式通常没什么意义,这里之所以提到只是为了说明 JSX 语法的完整性。

默认为“true”的 props

如果你没有给属性传值,那它默认为true。下面两种就是等价的:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

原文链接