JSX
JSX是對(duì)Javascript
的語法擴(kuò)展榜聂,而不是一種模板語言但狭。JSX不是字符串,也不是HTML棵帽。
1. 為什么會(huì)有JSX弓颈?
React認(rèn)為渲染邏輯跟UI邏輯(事件如何處理频鉴,狀態(tài)如何改變榄檬,數(shù)據(jù)如何展示)實(shí)際上天然相關(guān)的。
React并沒有使用傳統(tǒng)的將表現(xiàn)(markup)與邏輯(javascript)分離的方式刹悴,而是使用松耦合的組件將表現(xiàn)和邏輯組合到了一起行楞。
JSX實(shí)際上是組件渲染的語法糖,其糅合了標(biāo)記語言與UI對(duì)應(yīng)的優(yōu)點(diǎn)土匀,同時(shí)可以直接處理Javascript代碼子房。
ps:表現(xiàn)與處理邏輯分離仍舊有很大意義,Presentation Component與Container Component的提出正式這一原則的應(yīng)用。
在邏輯復(fù)雜的系統(tǒng)中证杭,將狀態(tài)抽取到Redux中進(jìn)行統(tǒng)一管理田度,將處理邏輯抽取到Container Component中,將UI展示抽取到Presentation Component中解愤。
實(shí)現(xiàn)了數(shù)據(jù)模型镇饺,處理邏輯,數(shù)據(jù)展示的分離送讲。
2. JSX的基本使用
2.1 JSX中可直接包含Javascript表達(dá)式
JSX將Javascript表達(dá)式包裹在大括號(hào){}中使用奸笤。
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')
);
2.2 JSX本身也是表達(dá)式
JSX在編譯后實(shí)際上是Javascript對(duì)象,因此可以直接在if或者for循環(huán)中使用哼鬓,也可以賦值給變量监右,或者作為函數(shù)參數(shù)傳遞,也可以作為函數(shù)返回值异希。
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
2.3 使用JSX中指定屬性
可以使用雙引號(hào)指定字符串字面量
const element = <div tabIndex="0"></div>;
也可以使用大括號(hào)指定Javascript表達(dá)式
const element = <img src={user.avatarUrl}></img>;
注意:JSX中的屬性使用camelCase(與HTML不同)
2.4 使用JSX指定Children
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
2.5 JSX可以阻止注入式攻擊
React DOM默認(rèn)在渲染前對(duì)JSX中的值進(jìn)行轉(zhuǎn)義健盒,這樣就避免了使用應(yīng)用中未顯式指定的值就行注入式攻擊,可以幫助防范XSS攻擊称簿。
3. JSX進(jìn)階
3.1 JSX的編譯
JSX提供了React.createElement(component,props,...children)
函數(shù)的語法糖扣癣。
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
會(huì)編譯成
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
<div className="sidebar" />
會(huì)編譯成
React.createElement(
'div',
{className: 'sidebar'},
null
)
3.2 指定React Element類型
JSX標(biāo)簽決定了React Element的類型,如果首字符是大寫憨降,那么代表是React Component搏色,如果首字符是小寫,那么是HTML DOM券册。
- React以及組件必須在作用域中
如果是React Component,那么標(biāo)簽的名稱就代表著保存React組件實(shí)例變量的名稱垂涯,因此此變量必須在當(dāng)前作用域中烁焙。
同時(shí),JSX編譯后也要使用到React變量耕赘,因此React變量也要保證在當(dāng)前作用域中骄蝇。
PS: 使用函數(shù)組件的時(shí)候,會(huì)忘記導(dǎo)入React模塊操骡,此時(shí)就會(huì)出現(xiàn)錯(cuò)誤提示(React is undefined)九火,其實(shí)就是因?yàn)槟K中未導(dǎo)入React。
import React from 'react';
import CustomButton from './CustomButton';
// React與CustomButton都需要在作用域中
function WarningButton() {
// return React.createElement(CustomButton, {color: 'red'}, null);
return <CustomButton color="red" />;
}
- 可使用點(diǎn)標(biāo)識(shí)符
如下面的MyComponents.DatePicker
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" />;
}
- 用戶自定義組件首字母需大寫
自定義組件的首字母需大寫册招,HTML支持的tag名稱都已經(jīng)作為React內(nèi)置的組件岔激,需小寫。
import React from 'react';
// Correct! This is a component and should be capitalized:
function Hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Correct! React knows <Hello /> is a component because it's capitalized.
return <Hello toWhat="World" />;
}
- 運(yùn)行時(shí)動(dòng)態(tài)選擇組件類型
如果需要?jiǎng)討B(tài)選擇組件類型是掰,那么將組件保存在常量中(首字母大寫)虑鼎,然后在JSX標(biāo)簽中可使用此變量名。
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
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} />;
}
3.3 JSX中的prop
在JSX中有一下幾種方式指定props
- 字符串字面量
使用引號(hào)包裹表示字符串字面量,所以下面兩種方式是等同的炫彩。
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
當(dāng)使用字符串字面量的時(shí)候匾七,其值會(huì)進(jìn)行HTML解析,所以下面兩個(gè)JSX表達(dá)式是等同的江兢。
<MyComponent message="<3" />
<MyComponent message={'<3'} />
- Javascript表達(dá)式
使用大括號(hào)包裹
<MyComponent foo={1 + 2 + 3 + 4} />
- 默認(rèn)為'True'
如果prop不指定值昨忆,那么其值默認(rèn)為true
(其表現(xiàn)跟HTML的disabled,checked,readOnly屬性類似)
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
- 延展屬性
如果已經(jīng)有props對(duì)象,可使用ES6的擴(kuò)展運(yùn)算符...
傳遞整個(gè)props對(duì)象杉允。以下兩種方式等同
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
...
運(yùn)算符也可以用來對(duì)props對(duì)象進(jìn)行分割邑贴,例如下面代碼提取kind
,將其他屬性放入新的對(duì)象other
夺颤。
const Button = props => {
const { kind, ...other } = props;
const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
return <button className={className} {...other} />;
};
const App = () => {
return (
<div>
<Button kind="primary" onClick={() => console.log("clicked!")}>
Hello World!
</Button>
</div>
);
};
延展屬性會(huì)傳遞不必要的props給組件(或者是無效的HTML屬性給DOM),所以痢缎,不要濫用。
3.4 JSX中的Children
JSX表達(dá)式包含一個(gè)打開標(biāo)簽和一個(gè)關(guān)閉標(biāo)簽世澜,在這兩個(gè)標(biāo)簽之前的內(nèi)容會(huì)作為一個(gè)特殊屬性傳遞props.children
独旷。
有以下幾種方式可以傳遞children:
- 字符串字面量
props.children就是字符串字面量"Hello World!"
<MyComponent>Hello world!</MyComponent>
HTML會(huì)被解析,所以轉(zhuǎn)義字符在JSX中跟HTML中使用方式一樣
<div>This is valid HTML & JSX at the same time.</div>
JSX會(huì)移除行首和行尾的空白字符,也會(huì)移除空白行寥裂,臨近標(biāo)簽的空白行也會(huì)移除嵌洼,字符串中的換行會(huì)被作為一個(gè)空格處理。
以下幾種方式是相同的
<div>Hello World</div>
<div>
Hello World
</div>
<div>
Hello
World
</div>
<div>
Hello World
</div>
想用JSX表示多個(gè)空格封恰,需要使用
- JSX表達(dá)式作為Children
可使用JSX元素作為Children麻养,便于組件嵌套
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
可以嵌套不同類型的children(like HTML)
<div>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
React組件可以返回元素?cái)?shù)組(不強(qiáng)制使用元素進(jìn)行包裹)
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
- Javascript表達(dá)式作為Children
可使用大括號(hào){}
直接包裹表達(dá)式作為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>
);
}
也可以跟其他類型混合使用
function Hello(props) {
return <div>Hello {props.addressee}!</div>;
}
- 函數(shù)作為Children
props.children
可以傳遞任何類型的數(shù)據(jù),例如函數(shù)诺舔,在React渲染之前將其
轉(zhuǎn)換為React可以識(shí)別的對(duì)象(字符串鳖昌,React元素,及其數(shù)組)即可低飒。
如下面ListOfTenThings
傳遞給Repeat
組件的props.children
是一個(gè)函數(shù)许昨,但是在Repeat
組件渲染前,將其轉(zhuǎn)換
為了React元素?cái)?shù)組items
// 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>;
}
function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}
- Boolean,Null,Undefined都將被忽略
false,true,null,undefined
都是有效的children,但是都不渲染,以下幾種方式相同
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
因此褥赊,可以使用&運(yùn)算符進(jìn)行條件渲染
<div>
{showHeader && <Header />}
<Content />
</div>
需要注意的是一些falsy value糕档,例如0
仍舊會(huì)被渲染。如果要作為渲染條件使用拌喉,請(qǐng)將其轉(zhuǎn)換為Boolean
.
此外速那,如果需要渲染false,null,true,undefined
這些值,可將其轉(zhuǎn)換為字符串