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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM