• JSX语法使用详解——终极版


    一、基础

    1、JSX是什么

    JSX是一种像下面这样的语法:

    const element = <h1>Hello, world!</h1>;

    它是一种JavaScript语法扩展,在React中可以方便地用来描述UI。

    本质上,JSX为我们提供了创建React元素方法(React.createElement(component, props, ...children))的语法糖(syntactic sugar)。上面的代码实质上等价于:

    var element = React.createElement(
      "h1",
      null,
      "Hello, world!"
    );

    2、JSX代表JS对象

    JSX本身也是一个表达式,在编译后,JSX表达式会变成普通的JavaScript对象。

    你可以在if语句或for循环中使用JSX,你可以将它赋值给变量,你可以将它作为参数接收,你也可以在函数中返回JSX。

    例如下面的代码:

    function getGreeting(user) {
      if (user) {
        return <h1>Hello, {formatName(user)}!</h1>;
      }
      return <h1>Hello, Stranger.</h1>;
    }

    上面的代码在if语句中使用JSX,并将JSX作为函数返回值。实际上,这些JSX经过编译后都会变成JavaScript对象。

    经过babel会变成下面的js代码:

    function test(user) {
        if (user) {
            return React.createElement(
                "h1",
                null,
                "Hello, ",
                formatStr(user),
                "!"
            );
        }
        return React.createElement(
            "h1",
            null,
            "Hello, Stranger."
        );
    }

    3、在JSX中使用JavaScript表达式

    在JSX中插入JavaScript表达式十分简单,直接在JSX中将JS表达式用大括号括起来即可。例如:

    function formatName(user) {
      return user.firstName + ' ' + user.lastName;
    }
    
    const user = {
      firstName: 'Harper',
      lastName: 'Perez'
    };
    
    const element = (
      <h1>
        Hello, {formatName(user)}!
      </h1>
    );
    
    ReactDOM.render(
      element,
      document.getElementById('root')
    );

    上面的代码中用到了函数调用表达式fromatName(user)。

    在JavaScript中,表达式就是一个短语,Javascript解释器会将其计算出一个结果,常量就是最简单的一类表达式。常用的表达式有:

    • 变量名;
    • 函数定义表达式;
    • 属性访问表达式;
    • 函数调用表达式;
    • 算数表达式;
    • 关系表达式;
    • 逻辑表达式;

    需要注意的是,if语句以及for循环不是JavaScript表达式,不能直接作为表达式写在{}中,但可以先将其赋值给一个变量(变量是一个JavaScript表达式):

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

    4、JSX属性值

    你可以使用引号将字符串字面量指定为属性值:

    const element = <div tabIndex="0"></div>;

    注意这里的”0”是一个字符串字面量。

    或者你可以将一个JavaScript表达式嵌在一个大括号中作为属性值:

    const element = <img src={user.avatarUrl}></img>;

    这里用到的是JavaScript属性访问表达式,上面的代码将编译为:

    const element = React.createElement("img", { src: user.avatarUrl });

    5、JSX的Children

    首先JSX可以是一个不包含Children的empty tag。如:

    const element = <img src={user.avatarUrl} />;

    JSX也可以像HTML标签一样包含Children

    const element = (
      <div>
        <h1>Hello!</h1>
        <h2>Good to see you here.</h2>
      </div>
    );

    这种写法在生成React元素的时候给我们带来了很大的便利,而且能够更加直观地描述UI。不然我们需要像下面这样创建和上面代码等价的React元素:

    const element = React.createElement(
      "div",
      null,
      React.createElement(
        "h1",
        null,
        "Hello!"
      ),
      React.createElement(
        "h2",
        null,
        "Good to see you here."
      )
    );

    tip: React DOM结点使用骆驼拼写法给属性命名

    例如:class在JSX中应写作className,tabindex应写作tabIndex。

    另外关于JSX的children需要注意的是:

    React自定义组件的chilren是不会像固有的HTML标签的子元素那样自动render的,我们看下面的例子:

    代码1
    class Test extends React.Component {
        render() {
          return (
            <div>
              Here is a list:
              <ul>
                <li>Item 1</li>
                <li>Item 2</li>
              </ul>
            </div>
          ) 
        }
    };
    ReactDOM.render(
        <Test />,
        document.getElementById('test')
    );

    以上代码定义的组件中都是build-in组件,类似div、p、ul、li等。它们中的子元素会直接render出来,像下面这样:

    但是如果你使用用户定义组件,比如:

    class Test extends React.Component {
        render() {
          return (
          <Em>
            Here is a list:
            <ul>
              <li>Item 1</li>
              <li>Item 2</li>
            </ul>
          </Em>
        ) 
        }
    };
    
    class Em extends React.Component {
      render() {
        return (<div></div>);
      }
    }
    
    ReactDOM.render(
        <Test />,
        document.getElementById('test')
    );

    并不能得到跟上面代码1一样的结果,我们得到的只是一个空的div标签:

    如果你想得到和代码1一样的结果,需要显示地指定props.children,像下面这样:

    class Test extends React.Component {
        render() {
          return (
              <Em>
                Here is a list:
                <ul>
                  <li>Item 1</li>
                  <li>Item 2</li>
                </ul>
              </Em>
          ) 
        }
    };
    
    class Em extends React.Component {
      render() {
        return (<div>{this.props.children}</div>);
      }
    }
    
    ReactDOM.render(
        <Test />,
        document.getElementById('test')
    );

    得到下面的结果:

    6、JSX可自动防范注入攻击

    在JSX中嵌入接收到的内容是安全的,比如:

    const danger = response.potentialDanger;
    
    cosnt ele = <h1>{title}</h1>

    在默认情况下,React DOM会将所有嵌入JSX的值进行编码。这样可以有效避免xss攻击。

    我们将以下代码编译后引入html:

    class Test extends React.Component {
        render() {
              let v = "<script></script>";
              return (
              <div>
                <h1>{v}</h1>
              </div>
            ) 
        }
    };
    
    ReactDOM.render(
        <Test />,
        document.getElementById('test')
    );

    得到结果是:

    可以看到通过JSX插入的文本自动进行了HTML转义,所以这里插入的是一段文本,而不是<script>标签。这有点类似于Js中的document.createTextNode("...")(实际上我们可以利用document.createTextNode进行HTML转义)。

    作为对比,换作使用DOM元素的innerHTML属性:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
        <div id="test"></div>
        <script type="text/javascript">
          document.getElementById("test").innerHTML="<h1><script></script></h1>";
        </script>
      </body>
    </html>

    得到结果如下:

    注意文本的颜色,此时插入的是一个<script>标签。

    如果你很清楚自己在做什么,希望直接将字符串不经转义编码直接插入到HTML文档流,可以使用dangerouslySetInnerHTML属性,这是一个React版的innerHTML,该属性接收一个key为__html的对象,修改代码如下:

          class Test extends React.Component {
                render() {
                  let v = {
                __html: "<script></script>"
              };
                  return (
                  <div>
                    <h1 dangerouslySetInnerHTML={v}/>
                  </div>
              ) 
                }
          };

    这次我们插入了<script>标签:

    二、进阶

    1、JSX中的props

    指定JSX中的props有以下几种方式:

    (1)使用JavaScript表达式

    任何有效的JavaScript表达式都可以作为prop的值,使用的时候将该表达式放在一对大括号中即可:

    <MyComponent foo={1 + 2 + 3 + 4} />
    
    <YourComponent clickTodo={(id) => this.props.handleClick(id)} />

    (2)使用字符串字面量

    字符串字面量可以作为prop值,下面的代码是等价的:

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

    (3)使用扩展运算符

    如果你想将一个prop对象传入JSX,你可以使用扩展运算符...直接将整个prop对象传入。下面的2个组件是等价的:

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

    扩展运算符是一个es6特性。是一种传递属性的十分便利的方式。但请注意不要滥用该运算符,注意不要将一大堆毫不相关的prop一股脑全部传入下面的组件中。

    共享结构的对象

    希望大家都知道这种 ES6 的语法:

    const obj = { a: 1, b: 2}
    const obj2 = { ...obj } // => { a: 1, b: 2 }
    

    const obj2 = { ...obj } 其实就是新建一个对象 obj2,然后把 obj 所有的属性都复制到 obj2 里面,相当于对象的浅复制。上面的 obj 里面的内容和 obj2 是完全一样的,但是却是两个不同的对象。除了浅复制对象,还可以覆盖、拓展对象属性:

    const obj = { a: 1, b: 2}
    const obj2 = { ...obj, b: 3, c: 4} // => { a: 1, b: 3, c: 4 },覆盖了 b,新增了 c
    

    我们可以把这种特性应用在 state 的更新上,我们禁止直接修改原来的对象,一旦你要修改某些东西,你就得把修改路径上的所有对象复制一遍,例如,我们不写下面的修改代码:

    appState.title.text = '《React.js 小书》'
    

    取而代之的是,我们新建一个 appState,新建 appState.title,新建 appState.title.text

    let newAppState = { // 新建一个 newAppState
      ...appState, // 复制 appState 里面的内容
      title: { // 用一个新的对象覆盖原来的 title 属性
        ...appState.title, // 复制原来 title 对象里面的内容
        text: '《React.js 小书》' // 覆盖 text 属性
      }
    }
    

    如果我们用一个树状的结构来表示对象结构的话:

    实例图片

    appState 和 newAppState 其实是两个不同的对象,因为对象浅复制的缘故,其实它们里面的属性 content 指向的是同一个对象;但是因为 title 被一个新的对象覆盖了,所以它们的 title 属性指向的对象是不同的。同样地,修改 appState.title.color

    let newAppState1 = { // 新建一个 newAppState1
      ...newAppState, // 复制 newAppState1 里面的内容
      title: { // 用一个新的对象覆盖原来的 title 属性
        ...newAppState.title, // 复制原来 title 对象里面的内容
        color: "blue" // 覆盖 color 属性
      }
    }
    

    实例图片

    我们每次修改某些数据的时候,都不会碰原来的数据,而是把需要修改数据路径上的对象都 copy 一个出来。这样有什么好处?看看我们的目的达到了:

    appState !== newAppState // true,两个对象引用不同,数据变化了,重新渲染
    appState.title !== newAppState.title // true,两个对象引用不同,数据变化了,重新渲染
    appState.content !== appState.content // false,两个对象引用相同,数据没有变化,不需要重新渲染
    

    修改数据的时候就把修改路径都复制一遍,但是保持其他内容不变,最后的所有对象具有某些不变共享的结构(例如上面三个对象都共享 content 对象)。大多数情况下我们可以保持 50% 以上的内容具有共享结构,这种操作具有非常优良的特性,我们可以用它来优化上面的渲染性能。

    2、JSX中的Children

    React组件中有一个特殊的prop–props.children。它指代了JSX表达式中开闭标签中包含的内容。

    下面讨论的是几种指定JSX的children的方法:

    (1)使用字符串字面量

    你可以直接在JSX的开闭标签中写入字符串字面量,组件得到的props.children就是该字符串值。

    以下面的代码为例:

    <MyComponent>Hello world!</MyComponent>

    MyComponent的props.chilren将获得”Hello World!”字符串。通过该方式传入的字符串是未经HTML转义的。实际上你只需要像在HTML标签中写入文本那样就可以了。例如你想在一对<p>标签中写入文本”<script></script>”,HTML和JSX写法是一样的,就像下面这样:

    <p>&#60;script&#62;&#60;/script&#62;</p>

    另外需要注意的是:

    JXS会自动删除一行中开头和结尾处的空白符;JSX会自动删除空行;JSX会删除紧邻标签的换行;JSX会删除字符串中的换行;字符串中的换行会被转换成一个空格

    举例来说,下面的JSX代码都是等价的:

    <div>Hello World</div>
    
    <div> Hello World   </div>
    
    <div>
      Hello World
    </div>
    
    <div>
      Hello
      World
    </div>
    
    <div>
    
      Hello World
    </div>

    (2)JSX元素作为children

    我们同样可以使用JSX元素作为JSX的children,由此生成嵌套组件:

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

    我们也可以混合使用字符串字面量和JSX作为children:

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

    El的props.children将得到一个数组:

    可以看到数组的第一个元素就是字符串“Here is a list:”,第二个元素是一个对象(JSX代表JavaScript对象)。

    (3)JavaScript表达式

    和prop一样,你也可以将任何有效的JavaScript表达式作为children传入,将它放在{}中就可以了。像下面这样:

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

    这里传入了一个常量表达式。

    下面使用一个函数调用表达式来生成一个list作为children:

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

    当然你也可以在一个字符串children中插入一个JavaScript表达式来生成一个“模板”:

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

    (4)函数children

    首先说明,这不是一种常见的用法。

    实际上,传入自定义组件的children并没有严格的限制,只要在React需要render的时候能将它们转换成可以render的东西就行了。

    下面是一个函数children的例子:

    function ListOfTenThings() {
      return (
        <Repeat numTimes={10}>
          {(index) => <div key={index}>This is item {index} in the list</div>}
        </Repeat>
      );
    }
    
    // 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>;
    }

    实际上,我们更通常的情况下是将(index) => <div key={index}>This is item {index} in the list</div>作为一个prop传入子组件。这个例子只是作为一种理解上的扩展。

    (5)有关布尔值、Null以及Undefined

    布尔值,Null以及Undefined可以作为有效的children,但他们不会被render,下面的JSX表达式都会render一个空的div标签:

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

    关于此有一个有趣的应用,在条件render中,下面的<Header />只有在show为true时才会render:

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

    3、注意事项

    (1)使用JSX时要引入React库

    前面已经解释过了,JSX是React.createElement方法的语法糖,因此在使用JSX的作用域中必须引入React库。

    如果你使用了JS打包工具,你可以在文件的头部作如下引用:

    import React from 'react';
    • 或者你不使用打包工具,也可以直接通过script标签引入React,比如:
    //本地
    <script src="./react.js"></script>
    
    //或者BootCDN
    <script src="http://cdn.bootcss.com/react/15.4.0/react.js"></script>

    此时React将作为一个全局变量被引入,变量名就是’React’。

    (2)注意引入JSX中用到的自定义组件

    JSX中用到的组件可能并不会在JavaScript中直接引用到,但自定义组件本质上就是一个JS对象,你在JSX中使用的时候,需要首先将该组件引入到当前作用域:

    import MyComponent from './MyComponent.js'
    
    ...
    
    <Outer>
      <MyComponent />
    </Outer>

    (3)自定义组件首字母一定要大写

    JSX中小写字母开头的element代表HTML固有组件如div,span,p,ul等。用户自定义组件首字母一定要大写如<Header><Picker>

    (4)元素标签名不能使用表达式

    下面的代码将产生错误:

    const components = {
      photo: PhotoStory,
      video: VideoStory
    };
    
    function Story(props) {
      // Wrong! JSX标签名不能使用表达式
      return <components[props.storyType] story={props.story} />;
    }

    如果你需要使用一个表达式来决定元素标签,你应该先将该表达式的值赋给一个大写字母开头的变量:

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

    (5)设置style属性

    在设置标签style属性的时候,要注意,我们是将一个描述style的对象以JavaScipt表达式的形式传入。因此应该有2层大括号:

    <div style={{color:'red', margin:'10px auto'}}></div>

    《一》、理解JSX

      JSXJavascrpt XML— — 一种在React组件内部构建标签的类XML语法。 
      JSX并不是新语言,也没有改变JavaScript的语法,只是一种基于ECMAScript的新特性,一种定义带属性树结构(DOM结构)的语法 
      React 可以不使用 JSX 来编写组件,但是使用 JSX 可以让代码可读性更高、语义更清晰、对 React 元素进行抽象等等。

    一、使用JSX好处

      1、允许使用熟悉的语法来定义HTML元素树  2、提供了更加语义化且易懂的标签 
      3、程序结构更容易被直观化  4、抽象了React Element的创建过程 
      5、可以随时掌控HTML标签以及生成这些标签的代码  63、是原生Javascript

    使用JSX和不使用JSX的代码对比如下:

    //开闭标签,在构建复杂的树形结构时,比函数调用和对象字面量更易读
    //#使用JSX
    <div className="red">Children Text</div>;
    <MyCounter count={3 + 5} />;
    // 此处给一个js对象设置"scores"属性
    var gameScores = {
      player1: 2,
      player2: 5
    };
    <DashboardUnit data-index="2">
      <h1>Scores</h1>
      <Scoreboard className="results" scores={gameScores} />
    </DashboardUnit>;
    
    //#不使用JSX
    React.createElement("div", { className: "red" }, "Children Text");
    React.createElement(MyCounter, { count: 3 + 5 });
    
    React.createElement(
      DashboardUnit,
      { "data-index": "2" },
      React.createElement("h1", null, "Scores"),
      React.createElement(Scoreboard, { className: "results", scores: gameScores })
    );

    二、JSX 语法

    【注意】 JSX代码需经babel 编译成javscript,目前主流浏览器才能正常识别,即在head中需添加:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>

    React还需依赖的另外2个文件:react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能, 
    并将JSX代码放在<script type='text/babel'> … </script>中编写

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
        <script src="build/react.js"></script>
        <script src="build/react-dom.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
      </head>
      <body>
        <div id="example"></div>
        <script type="text/babel">
          ReactDOM.render(
            <h1>Hello, world!</h1>,
            document.getElementById('example')
          );
        </script>
      </body>
    </html>
    •  

      JSXHTMLJavaScript混写的语法,当遇到<JSX就当HTML解析,遇到 { 就当JavaScript解析。

    var names = ['Alice', 'Emily', 'Kate'];
    ReactDOM.render(
      <div>
      {
        names.map(function (name) {
          return <div>Hello, {name}!</div>
        })
      }
      </div>,
      document.getElementById('example')
    );

     
    理解Array.prototype.map,返回一个新的数组

    var friends = ['Jake Lingwall', 'Murphy Randall', 'Merrick Christensen'];
    var listItems = friends.map(function(friend){
      return "<li> " + friend + "</li>";
    });
    console.log(listItems); // ["<li> Jake Lingwall</li>", "<li> Murphy Randall</li>", "<li> Merrick Christensen</li>"];

      JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员

    // arr变量是一个数组,结果 JSX 会把它的所有成员,添加到模板
    var arr = [
      <h1>Hello world!</h1>,
      <h2>React is awesome</h2>,
    ];
    ReactDOM.render(
      <div>{arr}</div>,
      document.getElementById('example')
    );


      在React中,一个组件的HTML标签与生成这些标签的代码内在地紧密联系在一起。这意味和我们可以轻松地利用Javascript强大魔力 
      1、使用三目运算符

    render: function() {
       return <div className={this.state.isComplete ? 'is-complete' : ''}>...</div>;
    }

      2、使用变量/函数

    getIsComplete: function() {
       return this.state.isComplete ? 'is-complete' : '';
    },
    render: function() {
       var isComplete = this.getIsComplete();
       return <div className={isComplete}>...</div>;
    
       //或者直接返回函数
       return <div className={this.getIsComplete()}>...</div>;
    }

      3、使用逻辑与(&&)或者逻辑或( || )运算符 
        由于对于null或false值React不会输出任何内容,因此可以使用一个后面跟随了期望字符串的布尔值来实现条件判断。如果这个布尔值为true,后续字符串会被使用

    render: function() {
       return <div className={this.state.isComplete && 'is-complete'}>...</div>;
    }

        逻辑或,this.props.name为空时,className值等于is-complete

    render: function() {
       return <div className={this.props.name || 'is-complete'}>...</div>;
    }

      4、函数调用

    var HelloWorld = React.createClass({
        render: function () {
            return <p>Hello, {(function (obj) { 
                return obj.props.name ? obj.props.name : "World";
            })(this)}</p>
        }
    });

    三、创建并渲染组件

    //1、React.createClass 是用来创建一个组件
    var HelloMessage = React.createClass({
      render: function() {
        return <h1>Hello {this.props.name}</h1>;
      }
    });
    //2、ReactDOM.render渲染组件
    ReactDOM.render(
      <HelloMessage name="John" />,
      document.getElementById('example')
    );
    《二》、JSX和HTML不同点

    一、渲染HTML标签和React组件

      React.render方法可以渲染HTML结构,也可以渲染React组件。 
        1、渲染HTML标签,声明变量采用首字母小写

    var myDivElement = <div className="foo" />;
    ReactDOM.render(myDivElement, document.body);

        2、渲染React组件,声明变量采用首字母大写

    var MyComponent = React.createClass({/*...*/});
    var myElement = <MyComponent someProperty={true} />;
    ReactDOM.render(myElement, document.body);

    二、非DOM属性

    1、键(key) 
      key是一个可选的唯一标识符,当两个已经在于DOM中的组件交换位置时,React能够匹配对应的key并进行相应的移动,且不需要完全重新渲染DOM,提供渲染性能 
     
    2、引用(ref) 
      ref允许父组件在render方法之外保持对子组件的一个引用。在JSX中,你可以通过属性中设置期望的引用名来定义一个引用。

    render: function() {
       return <div> 
                <input ref="myInput" ...> </input>
              </div>
        );
    }

      然后你可以在组件的任何地方使用this.refs.myInput获取这个引用了。通过引用获取到的这个对象被称为支持实例。它并不是真的DOM,而是React在需要时用来创建DOM的一个描述对象。你可以使用this.refs.myInput.getDOMNode()访问真实的DOM节点。

    3、设置原始的HTML 
      比如我们有一些内容是用户输入的富文本,希望展示相应的样式

    var content='<strong>content</strong>';
    ReactDOM.render(
        <div>{content}</div>,
        document.body
    );

    结果页面直接输出内容了: 
    这里写图片描述 
    React默认会进行HTML的转义,避免XSS攻击,如果要不转义,可以这么写:

    ReactDOM.render(
        <div dangerouslySetInnerHTML={{__html: "<strong>content</strong>"}}></div>,
        document.body
    );

    三、事件

      在所有的浏览器中,事件名已经被规范化并统一用onXXX驼峰形式表示

    var BannerAd = React.createClass({
        handleClick: function(event) {
            //...
        },
        render: function() {
           return <div onClick={this.handleClick}>Click Me!</div>;
        }
    })

    【注意】 React自动绑定了组件所有方法的作用域,因此你不需要手动绑定

    handleClick: function(event) {...},
    render: function() {
       //反模式 ————在React中手动给组件实例绑定
       //函数作用域是没有必要的
       return <div onClick={this.handleClick.bind(this)}>...</div>;
    }

    四、特殊属性

      由于JSX会转换为原生的Javascript函数,因此有一些关键词我们是不能用的——如classfor,对应JSX中属性写成classNamehtmlFor

    //#使用JSX
    ReactDOM.render(
        <label className="xxx" htmlFor="input">content</label>,
        document.getElementById('example')
    );
    //#不使用JSX
    ReactDOM.render(
        React.createElement('label', {className: 'xxx', htmlFor: 'input'}, 'content'),
        document.getElementById('example')
    );

    五、style样式

      React把所有的内联样式都规范化为驼峰形式,与JavascriptDOMsytle属性一致。第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象。

    React.render(
        <div style={{color:'red'}}>
            xxxxx
        </div>,
        document.body
    );
    ////////////////////////////
    var style = {
        color:'red',
        borderColor: '#999'
    }
    ReactDOM.render(<div style={style}>xxxxx</div>, document.body);

    六、注释

    JSX本质就是Javascript,因此你可以在标签内添加原生的javascript注释。

    • 注释两种形式 
      • 当作一个元素的子节点
      • 内联在元素的属性中
    var content = (
      <Nav>
        {/* 一般注释, 用 {} 包围 */}
        <Person
          /* 多
             行
             注释 */
          name={window.isLoggedIn ? window.name : ''} // 行尾注释
        />
      </Nav>
    );
    《三》、JSX延伸属性

    一、不要改变props

      如果提前就知道了组件的属性的话,写起来很容易。例如component组件有两个动态的属性foo和bar:

    var component = <Component foo={x} bar={y} />;
    •  

      而实际上,有些属性可能是后续添加的,我们没办法一开始就确定,我们可能会写出下面不好的代码:

    var component = <Component />;
    component.props.foo = x; // bad
    component.props.bar = y; // also bad

      这样写是错误的,因为我们手动直接添加的属性React后续没办法检查到属性类型错误,也就是说,当我们手动添加的属性发生类型错误时,在控制台是看不到错误信息的。

      在React的设定中,初始化完props后,props是不可变的。改变props会引起无法想象的后果。

    二、延伸属性

      为了解决这个问题,React引入了属性延伸

    var props = {};
    props.foo = x;
    props.bar = y;
    var component = <Component {...props} />;

      当需要拓展我们的属性的时候,定义个一个属性对象,并通过{...props}的方式引入,React会帮我们拷贝到组件的props属性中。重要的是—这个过程是由React操控的,不是手动添赋值的属性。

      需要覆盖的时候可以这样写:

    var props = { foo: 'default' };
    var component = <Component {...props} foo={'override'} />;
    console.log(component.props.foo); // 'override'
    《四》、JSX 陷阱

    一、自定义HTML属性

      如果往原生 HTML 元素里传入 HTML 规范里不存在的属性,React 不会显示它们。

    ReactDOM.render(
        <div dd='xxx'>content</div>,
        document.body
    );

    如果需要使用自定义属性,要加 data- 前缀。

    ReactDOM.render(
        <div data-dd='xxx' aria-dd='xxx'>content</div>,
        document.body
    );

    二、组件render渲染函数中return注意点 
    如果return和JSX语句开头在同一行时,不需要括号;否则要括号包裹

    //正确写法1
    var HelloMessage = React.createClass({
      render: function() {
        return <h1>
            Hello {this.props.name}</h1>;
      }
    });
    //正确写法2
    render: function() {
        return <h1>Hello {this.props.name}</h1>;
    }
    //正确写法3
    render: function() {
        return (
            <h1>Hello {this.props.name}</h1>);
    }
    
    //错误写法
    render: function() {
        return 
            <h1>Hello {this.props.name}</h1>;
    }

    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" />

    可以得到相同的编译结果。

    1.指定React元素的类型

    JSX标签的头部,决定了React元素的类型,大写的标签,意味着JSX的标签与React的组件一一对应,比如

    <Foo/>标签就对应了Foo组件

    (1)必须包裹在一定的范围内

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

    比如这样,引入了2个组件,构成了一个新的组件WarningButton,组件的返回值的元素,必须包含在一定范围内,这里通过函数的’{ ‘, ’ } ‘实现包裹的效果。

    (2)用户定义的组件必须大写

    我们前面已经说过,JSX的标签与组件是一一对应的,当我们使用JSX语法,引用组件的时候,标签必须要大写(同时定义组件的函数名也必须是大写的)。

    function Hello(){
       return <h2>Hello,World</h2>
    }
    //定义过程
    <Hello/>
    //使用过程

    (3)不能在运行期间,动态的选择类型 
    我们不能在JSX中,动态的规定组件的类型,举例来说:

    import React from 'react';
    import { PhotoStory, VideoStory } from './stories';
    
    const components = {
      photo: PhotoStory,
      video: VideoStory
    };
    
    function Story(props) {
      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) {
    
      const SpecificStory = components[props.storyType];
      return < SpecificStory  story={props.story} />;
        //这样就是正确的,我们不要在JSX的标签中使用动态定义
    }
    

    2.JSX中的Props属性

    (1)JS表达式 
    可以通过{},包裹js的语法来使用。比如:

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

    等价于:

    <MyComponent foo={10} />

    如果不是js表达式,则不能包裹在{}中使用。

    (2)Props属性的默认值 
    Props上的属性可以有默认值,并且默认值为true,比如:

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

    上面这两个式子是等价的,但是不推荐使用默认值,因为在ES6的语法中{foo}代表的意思是:{foo:foo}的意思,并不是{foo:true}。

    (3)扩展属性 
    可以通过ES6的…方法,给组件赋属性值,例如:

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

    上面的这两种方式是等价的。

    3.JSX中的children

    (1)children中的function

    我们来考虑自定义组件中包含函数的情况:

    function ListOfTenThings() {
      return (
        <Repeat numTimes={10}>
          {(index) => <div key={index}>This is item {index} in the list</div>}
        </Repeat>
      );
    }

    那么何时调用这个children中的方法呢?

    function Repeat(props) {
      let items = [];
      for (let i = 0; i < props.numTimes; i++) {
        items.push(props.children(i));
      }
      return <div>{items}</div>;
    }

    我们从上述的Repeat组件的定义中可以看出来,children中的方法按此定义会一直执行10次。

    (2)忽略Boolean,Null以及Undefined

    false,null,undefined以及true是不能通过render()方法,呈现在页面上的,下面的这些div块的样式 
    相同,都是空白块:

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

    这种属性,在通过render呈现元素的时候,是十分有用的,比如我们只想在div元素中展现Head组件, 
    例子如下:

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

    这里的逻辑是,只有showHeader==true,在会在页面呈现Header组件,否则为null,即为不显示任何东西,这相当于一个if的判断了。

    再举一个例子:

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

    在这个div中,我们需要知道的是即使元素为0,0是能够呈现在页面中的。也就是说上述代码中,只要 
    props.messages数组存在,不管长度是否为0都是存在的。(这里不同于js,js中的语法认为0==false)

    (3)如何显示Null,Undefined和Boolean

    如果我们一定要再页面上显示Null等,可以将其先转化为字符串之后再显示。

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

    通过String的转化后就能在页面上显示了。

    结语

    JSX在React中使用给我们带来了很大的便利,JSX的语法实际上十分简单也很容易掌握。祝学习愉快。

    欢迎点击 我的小站 
    本文地址 JSX语法详解

    一、为什么使用JSX

    1、JSX看起来像是XML的JavaScript语法扩展。React可以用来做简单的JSX语法转换。

    不需要为了React使用JSX可以使用JS创建,但建议使用JSX,因为可以使定义简洁且我们素质的包含属性的树状结构语法。XML有固定的标签开启和闭合,这能让复杂的树更易于阅读,优于方法调用和队形字面量的形式。

    二、JSX使用

    1、HTML标签与React组件对比

    React可以渲染HTML标签(strings)或者React组件(classes)。

    要渲染HTML标签:只需在JSX里使用小写字母开头的标签名。例如:

    var myDivElement= <div className="foo"/>;
    
    React.render(myDivElement,document.body);

    要渲染React组件只需创建一个大写字母开头的本地变量:

    var MyComponent =React.createClass({/*...*/});
    
     var myElement= <MyComponent someProperty={true}/>; React.render(myElement,document.body);

    React的JSX里约定分别使用首字母大小写区别本地组件的类和HTML标签。

    PS:由于JSX就是JavaScrript,一些标识想是class和for不建议作为XML属性名,作为代替,React DOM使用className和htmlfor来做对应的属性。

    2、转换

    JSX把类XML的语法转换成纯粹JavaScript,XML元素、属性、和子节点被转换成React.creatElement的参数。

    PS:JSX总是当作ReactElement执行,具体的实际细节可能不同。

    3、JavaScript表达式

    属性表达式:

    要使用JavaScript表达式作为属性值,只需要把这个表达式用一对大括号包起来,不要用(“”)。例如:

    //输入(JSX)
    
    var person =<Person name ={window.isLoggedIn?window.name:''}/>
    
    //输出(JS)
    
    var person =React.createElement(
    
    Person,
    
    {name:window.isLoggedIn?window.name : ''}
    
    };
    
    

    4、子节点表达式

    同样的JavaSCript表达式可用于表述子节点

    // 输入 (JSX):
    
    varcontent = <Container>{window.isLoggedIn? <Nav/> : <Login/>}</Container>;
    
     // 输出 (JS):
    
    varcontent =React.createElement(
    
     Container,
    
    null,
    
     window.isLoggedIn? React.createElement(Nav): React.createElement(Login)
    
     );

    注释

    JSX里添加注释很容易;他们只是JS表达式而已。你只要在一个标签的子节点内(非最外层)小心地用{}包围要注释的部分。


     

    var content = (
    
      <Nav>
    
        {/*一般注释,用 {}包围 */}
    
        <Person
    
          /*多
    
            行
    
            注释 */
    
          name={window.isLoggedIn ? window.name : ''} //行尾注释
    
        />
    
      </Nav>
    
    );
    
    

    三、JSX的延展属性

    1、

    varcomponent = <Component/>;

     component.props.foo= x; // 不好

     component.props.bar= y; // 同样不好

    这样是反模式出现错误React不会见长属性类型,有错误就不可以提示,props应该被禁止修改,修改后可能导致预料之外的结果。

    2、延展属性(Spread Attributes)

    var props ={};

    props.foo =x;

    props.bar=y;

    var component =<Component{...props} />;

    传入组件的属性会被复制到组件内。它能多次使用也可以与其他属性一起用。

    var props= {foo:‘default’}

    var component = <Component{...props}foo={'override'}/>;

    console.log(component.props.foo);//'override'

    PS: ...这三个点是操作符(也被叫做延展操作符-spread operator)已经被ES6数组支持。相关的还有ES7规定草案中的Object剩余和延展属性(Rest and Spread Properties)。

    四、JSX的陷阱

    1、JSX与HTML很相似但是还是存在一些关键的区别。

    首先与DOM的区别,如行内样式style。

    React 为了性能和跨浏览器的原因,实现了一个独立于浏览器的事件和 DOM 系统。利用此功能,可以屏蔽掉一些浏览器的 DOM 的粗糙实现。

    • 所有 DOM 的 properties 和 attributes (包括事件处理器)应该都是驼峰命名的,以便和标准的 JavaScript 风格保持一致。我们故意和规范不同,因为规范本身就不一致。然而,data-* 和 aria-* 属性符合规范,应该仅是小写的。
    • style 属性接收一个带有驼峰命名风格的 JavaScript 对象,而不是一个 CSS 字符串。这与 DOM 中的 style 的 JavaScript 属性保持一致,更加有效,并且弥补了 XSS 安全漏洞。
    • 所有的事件对象和 W3C 规范保持一致,并且所有的事件(包括提交事件)冒泡都正确地遵循 W3C 规范。参考事件系统获取更多详细信息。
    • onChange 事件表现得和你想要的一样:当表单字段改变了,该事件就被触发,而不是等到失去焦点的时候。我们故意和现有的浏览器表现得不一致,是因为 onChange 是它的行为的一个错误称呼,并且 React 依赖于此事件来实时地响应用户输入。参考表单获取更多详细信息。

    2、HTML实体

    可以插入到JSX的文本中。如果JSx表达表达式中显示HTML实体,可以回遇到二次转义的问题。因为React默认会转义所有字符串,为了防止各种XSS攻击。

    当:<div>First&middot; Second</div>时候会:
    
    // 错误: 会显示 “First &middot; Second”
    
    <div>{'First &middot; Second'}</div>

    有多种绕过的方法。最简单的是直接用 Unicode 字符。这时要确保文件是 UTF-8 编码且网页也指定为 UTF-8 编码。

    <div>{'First · Second'}</div>

    安全的做法是先找到 实体的 Unicode 编号 ,然后在 JavaScript 字符串里使用:

    <div>{'First u00b7 Second'}</div>
    
    <div>{'First ' + String.fromCharCode(183)+ ' Second'}</div>

    可以在数组里混合使用字符串和 JSX 元素:

    <div>{['First ', <span>&middot;</span>, ' Second']}</div>

    万万不得已时候使用原始的HTML。

    3、自定义HTML属性

    如果向原生HTML元素里传入HTML规范里不存在的属性,React不会显示它们。如果需要使用自定义属性,要加data-前缀

    <div data-custom-attribute="foo"/>

    以 aria- 开头的 [网络无障碍] 属性可以正常使用。

    <div aria-hidden={true}/>

    React普通样式(className)和行内样式(LineStyle)多种设置样式设置详解

    1.使用className设置样式(CSS的其他选择器也是同理)

    (1)定义一个CSS文件style.css,和普通CSS一样定义class选择器

    .sty1{//和普通CSS一样定义class选择器
    
    background-color: red;
    
    color: white;
    
    font-size: 40px;
    
    }

    (2)在JSX中导入编写好的CSS文件

    import './style.css';

    (3)JSX的调用

    <div className="sty1">看背景颜色和文字颜色</div>

    说明:id选择器也是同理,由于React使用ES6编写代码,而ES6又有class的概念,所以为了不混淆class选择器在React中写成了className
    可能你在看别人的代码的时候可能看到以下代码,这个是通过CSS Modules的写法
    (1)定义一个CSS文件styleother.css,和普通CSS一样定义class选择器

    .sty2{//和普通CSS一样定义class选择器
    
    background-color: red;
    
    color: white;
    
    font-size: 40px;
    
    }

    (2)在JSX中导入编写好的CSS文件

    import StyleOther from  './styleother.css';

    (3)JSX的调用

    <div className={StyleOther.sty2}>看背景颜色和文字颜色</div>

    说明:使用这种方式也是可以的,只是你需要修改你的webpack的config文件,将loader: "style!css"修改为loader: "style!css?modules",在css后面加一个modules,不过这两种方式是不能同时存在的,因为加了modules,
    详细资料:点击打开链接

    2.使用React的行内样式样式设置样式
    (1)在JSX文件中定义样式变量,和定义普通的对象变量一样

    let backAndTextColor = {
    
    backgroundColor:'red',
    
    color:'white',
    
    fontSize:40
    
    };

    (2)JSX的调用

    <div style={backAndTextColor}>看背景颜色和文字颜色</div>

    当然你也可以不定义一个变量,直接写到JSX中,如下代码所示:

    <div style={{backgroundColor:'red',color:'white',fontSize:40}}>看背景颜色和文字颜色</div>

    分析:style={},这里的{}里面放的是对象,不管你是定义一个对象然后使用,还是直接在里面写对象都是可以的,甚至可以写成下面的样子

    
    style={this.getInputStyles()}
    
    getInputStyles方法根据不同的状态返回不同的样式
    
    getInputStyles() {
    
    let styleObj;
    
    if (this.state.focused == true) {
    
    styleObj = {outlineStyle: 'none'};
    
    }
    
    return styleObj;
    
    }

    3.React行内样式扩展
    在 React 中,行内样式并不是以字符串的形式出现,而是通过一个特定的样式对象来指定。在这个对象中,key 值是用驼峰形式表示的样式名,而其对应的值则是样式值,通常来说这个值是个字符串,如果是数字就不是字符串,不需要引号。

    var divStyle = {
    
    color: 'white',
    
    backgroundImage: 'url(' + imgUrl + ')',
    
    WebkitTransition: 'all', // 注意这里的首字母'W'是大写
    
    msTransition: 'all' // 'ms'是唯一一个首字母需要小写的浏览器前缀
    
    };

    另外浏览器前缀除了ms以外首字母应该大写,所以这里的WebkitTransition的W是大写的。
    当为内联样式指定一个像素值得时候, React 会在你的数字后面自动加上 "px" , 所以下面这样的写法是有效的:

    let backAndTextColor = {
    
    backgroundColor:'red',
    
    color:'white',
    
    fontSize:40
    
    };

    有时候你_的确需要_保持你的CSS属性的独立性。下面是不会自动加 "px" 后缀的 css 属性列表:

    columnCount
    
    fillOpacity
    
    flex
    
    flexGrow
    
    flexShrink
    
    fontWeight
    
    lineClamp
    
    lineHeight
    
    opacity
    
    order
    
    orphans
    
    strokeOpacity
    
    widows
    
    zIndex
    
    zoom

    注释技巧:在React里注释不能用HTML的方式,那是木有用的。也不能直接用js的注释,那也是不行的。而是用大括号括起来,之后用/**/来注释,看起来是这样的
     

    {/* 这是一个注释 */}

    React普通样式(className)和行内样式(LineStyle)多种设置样式设置详解

    参考链接: 
    1、《React学习笔记—JSX》https://segmentfault.com/a/1190000002646155 
    2、《React引领未来的用户界面开发框架》 
    3、http://facebook.github.io/react/docs/jsx-in-depth.html 
    4、http://www.jikexueyuan.com/course/969_3.html?ss=1 
    5、http://buildwithreact.com/tutorial/jsx 

    正因为当初对未来做了太多的憧憬,所以对现在的自己尤其失望。生命中曾经有过的所有灿烂,终究都需要用寂寞来偿还。
  • 相关阅读:
    ant design拖拽手柄列拖动时样式错乱的解决方案
    ant design pro columns属性valueEnum下拉框按顺序显示
    blob转换为file上传(七牛云等)
    最新前端面试题收集(一)
    node 服务端分层模型小结
    将博客搬至CSDN
    Koa 连接mysql数据,mysql数据库表初始化脚本
    Koa 数据库连接和查询分离, CommonJS 模块遇到的一个坑
    koa session 存储方案
    koa-router 入门与使用
  • 原文地址:https://www.cnblogs.com/candlia/p/11920070.html
Copyright © 2020-2023  润新知