深入理解JSX
本文由筆者翻譯自官方文檔的JSX In Depth,若干案例經(jīng)過了改寫。其實(shí)說白了也好像不算太深入,但還是提示了一些可能的盲區(qū)。
JSX是為構(gòu)造React元素方法React.createElement(component, props, ...children)設(shè)計(jì)的語法糖。
比方說JSX代碼:
<Elem color="red" info="hello">Hello!<Elem/>
用標(biāo)準(zhǔn)的React語法寫出來應(yīng)該是:
React.creatElement({
Elem,
{
color:red,
info:"hello"
},
"Hello!"
});
當(dāng)沒有內(nèi)容時(shí),根據(jù)xml的語法特性,可以加上自閉合標(biāo)簽:
<Elem color="red" info="hello" />
那么React語法是這樣:
React.creatElement({
Elem,
{
color:red,
info:"hello"
},
null//啥內(nèi)容都沒有
});
如果你想測(cè)試JSX和javascript語法的轉(zhuǎn)換,可以點(diǎn)這里。
特殊的React元素規(guī)則
JSX標(biāo)簽的第一部分決定了React元素的類型。
JSX標(biāo)簽首字母必須要大寫,這樣做實(shí)際上聲明了:這個(gè)標(biāo)簽是一個(gè)React元素。
這個(gè)React標(biāo)簽被編譯成一個(gè)直接引用命名的變量,因此,當(dāng)你使用諸如 < Foo / >這樣的JSX表達(dá)式時(shí),Foo必須在React作用范圍內(nèi)。
必須在React作用范圍內(nèi)
當(dāng)你用React.createElement調(diào)用JSX, 必須引入React庫。
例如,下面的每個(gè)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打包工具或是不在腳本標(biāo)簽引用,上面這種寫法就讓你的標(biāo)簽就在React的全局作用范圍內(nèi)了。
JSX支持使用點(diǎn)符號(hào)
你也可以在React組件的JSX標(biāo)簽中使用點(diǎn)符號(hào)(.)。 如果你有一個(gè)專門用來輸出組件的模塊,里面塞滿了各種React組件,使用點(diǎn)符號(hào)調(diào)用會(huì)很方便。 比方說,如果MyComponents. DatePicker是一個(gè)組件,你可以直接用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" />;
}
用戶自定義的組件首字母必須大寫
如果一個(gè)React元素以小寫字母開頭,意味著它是一個(gè)內(nèi)置組件(如< div >或< span >)。結(jié)果是把一個(gè)字符串諸如“div”或“span”傳遞給React.createElement方法。 如果以大寫字母開頭,比如< Foo / >編譯的結(jié)果是:React.createElement(Foo),相應(yīng)地將生成一個(gè)自定義的組件,并導(dǎo)入到你的JavaScript文件中。
請(qǐng)以大寫字母命名組件。如果你不方便這么做,那就在使用JSX語法前編譯一下你的命名,分配一個(gè)大寫的變量名。
例如,這段代碼將不會(huì)像預(yù)期的那樣運(yùn)行:
import React from 'react';
// 錯(cuò)了,首字母必須大寫
function hello(props) {
// 正確!因?yàn)閐iv是html自帶的標(biāo)簽
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// 錯(cuò)了,React會(huì)把“hello”理解為一個(gè)html標(biāo)簽。
return <hello toWhat="World" />;
}
綜上,只要把上面代碼的hello改為Hello就可以了。
在運(yùn)行時(shí)選擇標(biāo)簽名
在React元素里面,你不能使用一般的javascript表達(dá)式。如果你想使用一個(gè)通用表達(dá)式來表示元素,先分配一個(gè)大寫變量名給它。你渲染一個(gè)基于props的組件時(shí),經(jīng)常會(huì)遇到這個(gè)問題:
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// 錯(cuò)了!JSX不接受表達(dá)式
return <components[props.storyType] story={props.story} />;
}
正確的做法是:
...
function Story(props) {
// 對(duì)啦。JSX可以接受一個(gè)大寫開頭的變量名
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
JSX中的props
有幾種不同的方式來指定在JSX中的props。
JavaScript表達(dá)式
props可以是任何javascript的表達(dá)式——但是得用{ }包起來。 例如,在這JSX中:
var a='zzz';
...
<App name={'xxx'+a+'yyy'}/>
App的props.name將會(huì)計(jì)算為xxxzzzyyy。
if語句h和for循環(huán)不是javascript的表達(dá)式,所以你不可以直接把它們作為props的值。相應(yīng)地,你可以這樣做:
...
var a=null;
if(this.props.b%2===0){
a=<div>偶數(shù)</div>
}else{
a=<div>奇數(shù)</div>
}
return (
<div>{this.props.b}是{a}</div>
)
...
字符串字面量
只要不是表達(dá)式,你就可以直接傳遞一個(gè)字符串作為props,而不沒有用花括號(hào)包著。 這兩個(gè)JSX表達(dá)式是等價(jià)的:
...
<App name="xxx<yyy"/>
//或者這樣
<App name={"xxx>yyy"}>
props默認(rèn)為True
如果你沒有給props傳值,那它默認(rèn)值就是true。如假包換的true。
所以以下兩個(gè)表達(dá)式基本上是等價(jià)的
//這樣可以
<App name={true}/>
//這樣也行
<App name/>
但是第二種用法在ES6語法中容易被混淆為<App name={name}/>,所以不建議這樣用。
傳播屬性
如果你已經(jīng)把props作為一個(gè)對(duì)象,你想用JSX統(tǒng)統(tǒng)添加進(jìn)組件,你可以使用…把整個(gè)props對(duì)象分享出去。 這兩個(gè)組件是等價(jià)的:
//寫法1
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
//寫法2:看起來是不是方便了很多呢?
function App2() {
var xxx = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...xxx} />;
}
當(dāng)你構(gòu)建一個(gè)大型的組件盒子,這個(gè)方法看起來很好用。但是{...xxx}方法太暴力了,可能一次性給你的組件傳遞了許多無關(guān)緊要的props——從而導(dǎo)致代碼混亂。因此在使用前應(yīng)該慎重。
JSX的children
JSX表達(dá)式中,必然包含一個(gè)開始標(biāo)記和結(jié)束標(biāo)記——開始標(biāo)簽和閉合標(biāo)簽之間的內(nèi)容是一個(gè)特殊的props——props.children。有以下幾種類型的數(shù)據(jù)都可以作為children:
字符串字面量
如果你在開始標(biāo)記之間放的是一個(gè)字符串,那么你的props.children就是這個(gè)字符串——而且還不用打引號(hào)。換言之你可以直接塞你需要的html內(nèi)容:
<App>Hello!</App>
<div>hello!</div>
//看起來和html沒區(qū)別吧
JSX還可以刪除在一行的開始和結(jié)束之間的空格。 同時(shí)還能刪除空白行。 行的相鄰標(biāo)記將被切掉。所以以下寫法都一樣:
<div>hello world!</div>
<div>hello
world!
</div>
<div>
hello
world!
</div>
JSX組件的子代
在組件嵌套時(shí),基本上你可以盡情地將子組件加進(jìn)去。這些嵌套的內(nèi)容都是props.children。
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
//或者這樣
<ul>
<li>haha</li>
<li>hehehe</li>
...
</ul>
但為什么說”基本上“呢?只有一個(gè)原則需要把握,就是一個(gè)React組件只能返回一個(gè)頂層對(duì)象——要么就用一個(gè)div包起來。這是入門時(shí)就反復(fù)提及的問題。
javascript表達(dá)式
javascript表達(dá)式用{}抱起來就可以了。這在遍歷方法時(shí)非常實(shí)用:
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>
);
}
});
函數(shù)
通常,JavaScript表達(dá)式插入JSX,將被解釋為一個(gè)字符串,或一個(gè)React元素,或一串這些東西。 更有趣的是,props.children的行為與其它props沒什么不同,不僅可以放用來渲染的內(nèi)容,還可以放函數(shù),方法。 例如,如果你有一個(gè)自定義組件,你可以把props.children作為回調(diào):
var ListTen=React.createClass({
content:function(index){
return (
<div key={index}>這是列表的第{index}項(xiàng)</div>
)
},
render:function(){
return (
<Repeat times={10}>
{this.content}
</Repeat>
);//這里把方法函數(shù)content作為一個(gè)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));
}//每循環(huán)一次就調(diào)用props.children的方法
return (
<div>{arr}</div>
);
}
});
ReactDOM.render(
<ListTen/>,
document.getElementById('example')
);
可見children可以是任何東西——只要React在渲染之前能夠理解它。盡管以上代碼思路不太常見,但是只要多嘗試,就會(huì)發(fā)現(xiàn)React的內(nèi)涵。
布爾值,空對(duì)象和未定義將被無視
false,?null,?undefined還有?true?對(duì)children來說都是可以放的。但是這對(duì)渲染而言沒有意義——React在渲染前簡(jiǎn)單地把他們忽略掉了。所以你用他們企圖改變渲染,什么也得不到:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{true}</div>
看起來好像什么都沒有用。但這是條件渲染的基本實(shí)現(xiàn)思路:
<div>
{showHeader && <Header />}
<Content />
</div
上面的代碼中,如果showHeader為false,那它將只執(zhí)行<Header/>。
此外常見的假值中,0是可以被渲染的。
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
如上,如果props.messages不存在,props.messages.length值就為0,結(jié)果0就被渲染進(jìn)去了。
因此,你最好保證你代碼中&&的前面是一個(gè)布爾值——比如props.messages.length>0就是一個(gè)不錯(cuò)的選擇。
對(duì)于被無視掉的內(nèi)容,這樣寫當(dāng)然是錯(cuò)的:
<div>
true
</div>
對(duì)于這個(gè)children,你應(yīng)該這樣寫——<div>{'true'}</div>,如果是變量,就用字符串方法把它轉(zhuǎn)一下。
總結(jié)
- 上一篇: 电脑CPU如何超频台式电脑如何超频
- 下一篇: 「美团外卖APP签约快捷支付」流程体验