爲什麼要引入React
import React from 'react';
function A() {
return (
<h1>魚魚dev</h1>
)
}
在這裏我們並沒有使用到React變量,那爲什麼還要引用React進來呢?
可以試一下,如果我們省略了React的引入操作import React from 'react';
,就會報以下的錯誤:
我們經過babel轉化後,會將上述代碼轉換成:
function A() {
return React.createElement("h1", null, "魚魚dev");
}
所以可以看出來,JSX只是React.createElement(component, props, ...children)
的語法糖。
爲什麼要用className而不用class
React一開始的理念是想與瀏覽器的DOM API保持一致,而不是與HTML保持一致。所以更願意選擇DOM API中的className屬性。
爲什麼屬性要用小駝峯
這裏提一句,駝峯命名分爲大駝峯和小駝峯,大駝峯爲首字母爲大寫,其餘每個單詞首字母大寫。小駝峯爲首字母小寫,其餘每個單詞首字母小寫。還有另外兩種主流的命名規則,一是下劃線連接符,一是橫槓連接符。
同上,React的理念是與瀏覽器的DOM API保持一致,也就使用了DOM API中的小駝峯命名的風格。
爲什麼constructor裏要調用super以及傳入props
這裏有兩個問題
- 爲什麼constructor裏必須要調用
super
? - 爲什麼super裏必須傳入
props
?
爲什麼constructor裏必須要調用super
?
其實這不是React的限制,而是JavaScript的限制。要想在構造函數裏調用this
,必須得在此之前調用過super。這裏的原因涉及到了JavaScript的”繼承“。
如若沒有調用super:
JavaScript是通過原型鏈來實現繼承的,在此我們不深究原型鏈,單說在原型鏈中super的意義。super指代着父類的構造函數,在子類的構造函數中調用super()
,則意味着調用了父類的構造函數,從而使得創建子類的實例時,不光有子類的屬性,還會有父類的屬性。
所以說,如過說在子類的構造函數中沒有調用super
的話,則子類的實例中不會有父類的屬性值。這就使得這個繼承名不副實了。所以JavaScript規定繼承時,必須得在子類的構造函數中調用super
。
爲什麼super裏必須傳入props
你必須得在super調用時傳入props
才能在構造函數中使用this.props
,否則使用this.props
會報undefined沒有這個屬性。不過在構造函數之後,如render函數中卻能在其中使用this.props
。
爲什麼呢?
這是因爲在React的構造函數被調用之後,會給創建的React實例對象綁入一個props
屬性,其值就是props
。
const instance = new YourComponent(props);
instance.props = props
不過由於是在構造函數被調用後,才綁如props屬性,也就是說在構造函數執行時,this
是沒有props
這個尚需經的。
所以說,還是得在super中傳入props,否則就無法在構造函數中使用this.props
。
class App extends React.Component {
constructor (props) {
super(props);// 既調用了super,又傳入了props
console.log(this.props);// 可以訪問到值,並且不會報錯
}
render () {
console.log(this.props);// 可以訪問到值
return <h1>魚魚dev</h1>
}
}
class App extends React.Component {
constructor (props) {
super();// 調用了super,不傳入props
console.log(this.props);// undefined
}
render () {
console.log(this.props);// 可以訪問到值
return <h1>魚魚dev</h1>
}
}
class App extends React.Component {
constructor (props) {
// 既不調用super,也不傳入props
console.log(this.props);// 這裏就直接報錯了
}
render () {
console.log(this.props);
return <h1>魚魚dev</h1>
}
}
得益於React中的babel的強大,babel提供了es6中都不支持的實例屬性的寫法,也就是說實例屬性你不光可以在構造函數中聲明,還可以像這樣寫:
class A {
a = '1'
}
而如果用這種寫法,也就不用在類中寫構造函數了。所以繼承也不用手動調用super來繼承父類的實例屬性,默認就幫你調用好了。
class A {
a = '1'
}
class B extends A {
b = '2'
}
console.log(new B().a);// '1'
所以React中可以這樣寫:
class App extends React.Component {
render () {
console.log(this.props);// 有值
return <h1>魚魚dev</h1>
}
}
爲什麼組件名要用大寫開頭
前面有提到,JSX是React.createComponent(component, props, ...children)
的語法糖,在這裏component
的類型可以是string或ReactClasstype。
- 當component值爲string類型,react會覺得他是原生dom節點
- 當component值爲ReactClasstype,react會覺得他是自定義組件
這是在React.createComponent(component, props, ...children)
,而在JSX中是如何區別是string還是ReactComponent呢?如果是大寫開頭,是ReactComponent,如果是小寫開頭,是string。
function A() {
// 小寫開頭,React認爲它是原生dom節點。babel後
// 爲React.createComponent("div",null);
return <div></div>
}
import MyComponent from './myComponent.js';
function A() {
// 大寫開頭,React認爲他是自定義組件。babel後
// 爲React.createComponent(MyComponent,null);
return <MyComponent></MyComponent>
}
import myComponent from './myComponent.js';// 這裏有個改動,引入自定義組件名小寫開頭
function A() {
// 小寫開頭,React認爲它是原生dom節點。babel後
// 爲React.createComponent("myComponent",null);
// 但由於dom節點中沒有myComponent,則報錯
return <myComponent></myComponent>
}
若你本身是想用ReactComponent,但卻一小寫開頭:
爲什麼調用方法要 bind this
相信如果大家有研究過JavaScript的this
指向問題的話,都知道像下面這種情況,函數中的this指向會丟失:
const a = {
func: function () {
console.log(this);
}
}
const nextA = a.func;
nextA();// 打印undefined(嚴格模式下)
a.func();// 打印{func: f}
這就是大家常說的this指針丟失的問題。
React中的事件綁定也是這樣的:
class Foo extends React.Component {
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
<!-- 在這裏我綁定事件未bind(this),結果是當觸發click事件,執行handleClick函數時,內部this指向爲undefined -->
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
這是因爲onClick={this.handleClick}
實際上是分爲兩步的:
const handleClick = this.handleClick;
...onClick = handleClick
這就發生了上文所說的this指針丟失。
所以必須得在一開始就確定死this指向,以保證即使執行了const handleClick = this.handleClick;
`...onClick = handleClick`也不會發現this指針丟失的現象。
具體方法有一下兩種:
class Foo extends React.Component {
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
// 在綁定的時候再bind(this)。性能不是很好,多處地方調用同一
// 函數的話,得重複bind
<button onClick={this.handleClick.bind(this)}>
Click me
</button>
)
}
}
class Foo extends React.Component {
constructor () {
// 在構造函數裏bind(this)。缺點就是寫起來不順手......
// 沒有人會習慣在類裏聲明瞭函數,再去構造函數裏去bind一次把
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
除這兩種方法,還有一種方法:箭頭函數可以解決這一問題(箭頭函數的this指向完全繼承於上一作用域的this指向),箭頭函數又有兩種寫法:
class Foo extends React.Component {
constructor () {
// 在構造函數裏bind(this)。缺點就是寫起來不順手......
// 沒有人會習慣在類裏聲明瞭函數,再去構造函數裏去bind一次把
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
// 相當於每次點擊事件的時候就聲明一個匿名函數,這個比第一種方法還費性能
<button onClick={e => {this.handleClick}}>
Click me
</button>
)
}
}
class Foo extends React.Component {
// 這是我最喜歡的寫法了,美觀而又省性能
handleClick = () => {
this.setState({ xxx: aaa })
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
可能大家會想,爲什麼React不自己bind呢?因爲每次調用的時候,都bind一次,會影響性能,倒不如一開始就bind好,然後在調用時候直接調用。