一、基礎
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><script></script></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 |
JSX即Javascrpt 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>
JSX是HTML和JavaScript混寫的語法,當遇到<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函數,因此有一些關鍵詞我們是不能用的——如class和for,對應JSX中屬性寫成className和htmlFor
//#使用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把所有的內聯樣式都規范化為駝峰形式,與Javascript中DOM的sytle屬性一致。第一重大括號表示這是 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
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 依賴於此事件來實時地響應用戶輸入。參考表單獲取更多詳細信息。
- 表單輸入屬性,例如 value 和 checked,以及 textarea。這里有更多相關信息。
2、HTML實體
可以插入到JSX的文本中。如果JSx表達表達式中顯示HTML實體,可以回遇到二次轉義的問題。因為React默認會轉義所有字符串,為了防止各種XSS攻擊。
當:<div>First· Second</div>時候會:
// 錯誤: 會顯示 “First · Second”
<div>{'First · 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>·</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


