• 深入理解JSX


    本文由笔者翻译自官方文档的JSX In Depth,若干案例经过了改写。其实说白了也好像不算太深入,但还是提示了一些可能的盲区。

    JSX是为构造React元素方法React.createElement(component, props, ...children)设计的语法糖。

    比方说JSX代码:

    <Elem color="red" info="hello">Hello!<Elem/>
    

    用标准的React语法写出来应该是:

    React.creatElement({
      Elem,
      {
        color:red,
      	info:"hello"
      },
      "Hello!"
    });
    

    当没有内容时,根据xml的语法特性,可以加上自闭合标签:

    <Elem color="red" info="hello" />
    

    那么React语法是这样:

    React.creatElement({
      Elem,
      {
        color:red,
      	info:"hello"
      },
      null//啥内容都没有
    });
    

    如果你想测试JSX和javascript语法的转换,可以点这里


    特殊的React元素规则

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

    JSX标签首字母必须要大写,这样做实际上声明了:这个标签是一个React元素。

    这个React标签被编译成一个直接引用命名的变量,因此,当你使用诸如 < Foo / >这样的JSX表达式时,Foo必须在React作用范围内。

    必须在React作用范围内

    当你用React.createElement调用JSX, 必须引入React库。

    例如,下面的每个import都是必要的——尽管React和CustomButton不是直接从JavaScript引用:

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

    如果你不使用javascript打包工具或是不在脚本标签引用,上面这种写法就让你的标签就在React的全局作用范围内了。

    JSX支持使用点符号

    你也可以在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" />;
    }
    

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

    如果一个React元素以小写字母开头,意味着它是一个内置组件(如< div >或< span >)。结果是把一个字符串诸如“div”或“span”传递给React.createElement方法。 如果以大写字母开头,比如< Foo / >编译的结果是:React.createElement(Foo),相应地将生成一个自定义的组件,并导入到你的JavaScript文件中。

    请以大写字母命名组件。如果你不方便这么做,那就在使用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" />;
    }
    

    综上,只要把上面代码的hello改为Hello就可以了。

    在运行时选择标签名

    在React元素里面,你不能使用一般的javascript表达式。如果你想使用一个通用表达式来表示元素,先分配一个大写变量名给它。你渲染一个基于props的组件时,经常会遇到这个问题:

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

    正确的做法是:

    ...
    function Story(props) {
      // 对啦。JSX可以接受一个大写开头的变量名
      const SpecificStory = components[props.storyType];
      return <SpecificStory story={props.story} />;
    }
    

    JSX中的props

    有几种不同的方式来指定在JSX中的props。

    JavaScript表达式

    props可以是任何javascript的表达式——但是得用{ }包起来。 例如,在这JSX中:

    var a='zzz';
    ...
    <App name={'xxx'+a+'yyy'}/>
    

    Appprops.name将会计算为xxxzzzyyy

    if语句h和for循环不是javascript的表达式,所以你不可以直接把它们作为props的值。相应地,你可以这样做:

    ...
    var a=null;
    
    if(this.props.b%2===0){
      a=<div>偶数</div>
    }else{
      a=<div>奇数</div>
    }
    return (
    	<div>{this.props.b}是{a}</div>
    )
    ...
    

    字符串字面量

    只要不是表达式,你就可以直接传递一个字符串作为props,而不没有用花括号包着。 这两个JSX表达式是等价的:

    ...
    <App name="xxx&lt;yyy"/>
    //或者这样
    <App name={"xxx>yyy"}>
    

    props默认为True

    如果你没有给props传值,那它默认值就是true。如假包换的true。

    所以以下两个表达式基本上是等价的

    //这样可以
    <App name={true}/>
    //这样也行
    <App name/>
    

    但是第二种用法在ES6语法中容易被混淆为<App name={name}/>,所以不建议这样用。

    传播属性

    如果你已经把props作为一个对象,你想用JSX统统添加进组件,你可以使用把整个props对象分享出去。 这两个组件是等价的:

    //写法1
    function App1() {
      return <Greeting firstName="Ben" lastName="Hector" />;
    }
    //写法2:看起来是不是方便了很多呢?
    function App2() {
      var xxx = {firstName: 'Ben', lastName: 'Hector'};
      return <Greeting {...xxx} />;
    }
    

    当你构建一个大型的组件盒子,这个方法看起来很好用。但是{...xxx}方法太暴力了,可能一次性给你的组件传递了许多无关紧要的props——从而导致代码混乱。因此在使用前应该慎重。


    JSX的children

    JSX表达式中,必然包含一个开始标记和结束标记——开始标签和闭合标签之间的内容是一个特殊的props——props.children。有以下几种类型的数据都可以作为children

    字符串字面量

    如果你在开始标记之间放的是一个字符串,那么你的props.children就是这个字符串——而且还不用打引号。换言之你可以直接塞你需要的html内容:

    <App>Hello!</App>
    
    <div>hello!</div>
    //看起来和html没区别吧
    

    JSX还可以删除在一行的开始和结束之间的空格。 同时还能删除空白行。 行的相邻标记将被切掉。所以以下写法都一样:

    <div>hello world!</div>
    
    <div>hello 
    world!
    </div>
    
    <div>
      
      hello
      world!
    </div>
    

    JSX组件的子代

    在组件嵌套时,基本上你可以尽情地将子组件加进去。这些嵌套的内容都是props.children

    <MyContainer>
      <MyFirstComponent />
      <MySecondComponent />
    </MyContainer>
    
    //或者这样
    <ul>
      <li>haha</li>
      <li>hehehe</li>
      ...
    </ul>
    

    但为什么说”基本上“呢?只有一个原则需要把握,就是一个React组件只能返回一个顶层对象——要么就用一个div包起来。这是入门时就反复提及的问题。

    javascript表达式

    javascript表达式用{}抱起来就可以了。这在遍历方法时非常实用:

    var App=React.createClass({
      render:function(){
        var arr=[];
        for(var i=0;i<6;i++){
          var content=<li key={i.toString}>我是第{i+1}行</li>
          arr.push(content);
        }
        
        return (
        	<ul>{arr}</ul>
        );
      }
    });
    

    函数

    通常,JavaScript表达式插入JSX,将被解释为一个字符串,或一个React元素,或一串这些东西。 更有趣的是,props.children的行为与其它props没什么不同,不仅可以放用来渲染的内容,还可以放函数,方法。 例如,如果你有一个自定义组件,你可以把props.children作为回调:

    var ListTen=React.createClass({
            content:function(index){
                return (
                    <div key={index}>这是列表的第{index}项</div>
                )
            },
            render:function(){
                return (
                    <Repeat times={10}>
                      {this.content}
                    </Repeat>
                );//这里把方法函数content作为一个Repeat组件的props.children
    
            }
        });
    	
        var Repeat=React.createClass({
            render:function(){
                var arr=[];
                for(var i=0;i<this.props.times;i++){
                  arr.push(this.props.children(i));
                }//每循环一次就调用props.children的方法
    
                return (
                    <div>{arr}</div>
                );
            }
        });
    
        ReactDOM.render(
            <ListTen/>,
            document.getElementById('example')
        );
    

    可见children可以是任何东西——只要React在渲染之前能够理解它。尽管以上代码思路不太常见,但是只要多尝试,就会发现React的内涵。

    布尔值,空对象和未定义将被无视

    falsenullundefined还有 true 对children来说都是可以放的。但是这对渲染而言没有意义——React在渲染前简单地把他们忽略掉了。所以你用他们企图改变渲染,什么也得不到:

    <div />
    
    <div></div>
    
    <div>{false}</div>
    
    <div>{null}</div>
    
    <div>{true}</div>
    

    看起来好像什么都没有用。但这是条件渲染的基本实现思路:

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

    上面的代码中,如果showHeader为false,那它将只执行<Header/>

    此外常见的假值中,0是可以被渲染的。

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

    如上,如果props.messages不存在,props.messages.length值就为0,结果0就被渲染进去了。

    因此,你最好保证你代码中&&的前面是一个布尔值——比如props.messages.length>0就是一个不错的选择。

    对于被无视掉的内容,这样写当然是错的:

    <div>
    	true  
    </div>
    

    对于这个children,你应该这样写——<div>{'true'}</div>,如果是变量,就用字符串方法把它转一下。

  • 相关阅读:
    Android跨进程通信AIDL服务*
    Android跨进程通信广播(Broadcast)*
    Android跨进程通信Content Provider*
    Android跨进程通信访问其他应用程序的Activity*
    Android RecyclerView实现加载多种条目类型*
    Android ListView多布局*
    Android屏幕横竖屏切换和生命周期管理的详细总结*
    Android中的asserts和res/raw资源目录*
    Android系统服务 —— WMS与AMS*
    Android中为什么主线程不会因为Looper.loop()方法造成阻塞
  • 原文地址:https://www.cnblogs.com/djtao/p/6200866.html
Copyright © 2020-2023  润新知