• React文档(十四)深入JSX


    根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖。JSX代码是这样的:

    <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组件。这些标签被编译成同名变量并被引用,因此如果你使用JSX <Foo />表达式,必须在作用域之内声明Foo变量。

    React必须声明

    由于 JSX 编译后会调用 React.createElement 方法,所以在你的 JSX 代码中必须首先声明 React 变量。
    举个例子,所有的引入模块都很重要,即使React和CustomButton不是直接在代码里调用:
    import React from 'react';
    import CustomButton from './CustomButton';
    
    function WarningButton() {
      // return React.createElement(CustomButton, {color: 'red'}, null);
      return <CustomButton color="red" />;
    }

    如果你不使用一个js打包器而是把React以一个script标签添加,那么React就已经在作用域内,作为一个全局对象。

    点表示法

    你也可以在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。以一个大写字母开头的就像<Foo />编译为React.createElement(Foo)并且和一个在js文件里定义或引入的组件相一致。
    我们建议将组件的首字母大写。如果你的确有一个组件是以小写字母开头,那就在使用之前把它指派给一个首字母大写的变量。
    举个例子,这段代码不会如预期运行:
    import React from 'react';
    
    // 错误!组件名应该首字母大写:
    function hello(props) {
      //正确,div是有效的html标签:
      return <div>Hello {props.toWhat}</div>;
    }
    
    function HelloWorld() {
      // 错误!React 会将小写开头的标签名认为是 HTML 原生标签:
      return <hello toWhat="World" />;
    }

    为了解决这个问题,我们会重命名hello为Hello并且在渲染的时候使用<Hello />。

    import React from 'react';
    
    // 正确!组件名应该首字母大写:
    function Hello(props) {
      // 正确,div是有效的html标签:
      return <div>Hello {props.toWhat}</div>;
    }
    
    function HelloWorld() {
      // 正确!React 能够将大写开头的标签名认为是 React 组件.
      return <Hello toWhat="World" />;
    }

    运行时选择类型

    你不能使用一个普通的表达式来当做React元素的标签。如果你想要使用一个普通的表达式去表示元素的类型,那么就把它赋值给一个首字母大写的变量。这种情况通常发生在你想要基于同一个prop来渲染一个不同的组件:
    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} />;
    }

    JSX里的props

    有许多不同的方法在JSX里指定props。

    js表达式

    你可以将任意js表达式作为一个prop来传递,通过用{}包裹表达式。举个例子,在这段JSX里:

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

    对于MyComponent,props.foo的值会是10因为表达式1+2+3+4被求值了。

    if语句和for循环在js里不算表达式,所以他们在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'} />

    当你传递了一个字符串字面值,该值会被解析为HTML非转义字符串。所以下面两个JSX表达式是相等的:

    <MyComponent message="&lt;3" />
    
    <MyComponent message={'<3'} />

    这个行为通常不是有重大意义的,这里提及只是因为完整性。

    props默认是true

    如果你不给prop传递任何值,那么它默认就是true。下面两个JSX表达式是相等的:

    <MyTextBox autocomplete />
    
    <MyTextBox autocomplete={true} />

    通常情况下,我们不建议使用这个因为ES6对象简洁表示法会造成困惑。{foo}是{foo: foo}的简写形式而不是{foo: true}。这里这样用是因为这个行为和HTML的行为匹配。

    扩展属性

    如果你已经有了一个对象props,并且你想要把它传递给JSX,你可以使用...扩展运算符来传递整个props对象。下面两个组件是等同的:

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

    扩展属性当你在创建通用容器的时候会很有用。然而,他们也会把你的代码弄乱,因为它们很容易传递很多不相关的props给根本不关心它们的组件。我们建议你保守地使用此语法。

    JSX里的children

    在同时包含起始标签和结束标签的JSX表达式里,这些标签之间的内容被作为特殊的prop来传递:props.children。传递children有几种不同的方式:

    字符串常量

    你可以在起始标签和结束标签之间放一个字符串props.children。这对于许多内建HTML元素来说很有用。举个例子:

    <MyComponent>Hello world!</MyComponent>

    这是有效的JSX,而且MyComponent组件的props.children就是那个字符串“Hello world!”。HTML未转义,所以你可以像写HTML一样来写JSX:

    <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 children

    你可以提供更多的JSX元素来作为子元素。这样在显示嵌套的组件时会很有用:

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

    你也可以混合不同类型的子元素,所以你可以将JSX子组件和字符串字面量一起使用。这样使JSX像HTML的语法,就可以同时使JSX和HTML都有效:

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

    React 组件也可以通过数组的形式返回多个元素:

    render() {
      // 不需要使用额外的元素包裹数组中的元素
      return [
        // 不要忘记 key :)
        <li key="A">First item</li>,
        <li key="B">Second item</li>,
        <li key="C">Third item</li>,
      ];
    }

    js表达式

    你可以传递任意js表达式作为子元素,通过使用{}来包裹表达式。举个例子,以下表达式是等同的:

    <MyComponent>foo</MyComponent>
    
    <MyComponent>{'foo'}</MyComponent>
    通常渲染一组任意长度的JSX表达式这样子是很有用的。举个例子,这样做渲染了一个HTML列表:
    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>
      );
    }

    js表达式可以与其他类型子元素混合。这样在字符串模板的场景里比较常用:

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

    函数作为子元素

    通常情况下,插入JSX的js表达式会被认为是一个字符串,一个React元素,或者一组这些东西。然而,props.children运作的时候就和其他属性一样可以传递任意一种数据,不仅仅是React元素。举个例子,如果你有一个自定义组件,你可以可以将调用 props.children 来获得传递的子代:
    // 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>;
    }
    
    function ListOfTenThings() {
      return (
        <Repeat numTimes={10}>
          {(index) => <div key={index}>This is item {index} in the list</div>}
        </Repeat>
      );
    }

    给自定义组件传递的children可以是任何东西,只要组件在渲染之前将它们转变成React可以理解的即可。这个children的用法不常见,但是它是可以运行的只要你想要扩展JSX的能力。

    布尔值,null值还有undefined是被忽略的

    false,null,undefined还有true是有效的children。但是它们不会被渲染。以下这些JSX表达式都是等价的:
    <div />
    
    <div></div>
    
    <div>{false}</div>
    
    <div>{null}</div>
    
    <div>{undefined}</div>
    
    <div>{true}</div>

    这样根据条件是否渲染React元素就可以很有用。下面JSX渲染一个<Header />如果showHeader的值是true:

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

    值得注意的就是一些会被转换成false的值,类似于数字0,依然会被React渲染。举个例子,下面的代码也许不会像你期待的那样运行因为当props.messages是一个空数组的时候0将会被打印:

    <div>
      {props.messages.length &&
        <MessageList messages={props.messages} />
      }
    </div>
    要修复这个问题,确保在&&之前的表达式总是一个布尔值:
    <div>
      {props.messages.length > 0 &&
        <MessageList messages={props.messages} />
      }
    </div>

    相反的,如果你想要让一个值像false,true,null或者undefined出现在输出中,你必须首先将它转换成字符串:

    <div>
      My JavaScript variable is {String(myVariable)}.
    </div>
  • 相关阅读:
    saltstack总结-2018-0620
    我的书籍《Redis 源码日志》
    深入剖析 redis 主从复制
    深入剖析 redis AOF 持久化策略
    初探单点登录 SSO
    深入剖析 redis RDB 持久化策略
    深入剖析 redis 事件驱动
    memcached 源码阅读笔记
    Django 源码小剖: Django ORM 查询管理器
    Django 源码小剖: Django 对象关系映射(ORM)
  • 原文地址:https://www.cnblogs.com/hahazexia/p/6428011.html
Copyright © 2020-2023  润新知