React 基礎(chǔ)知識介紹
本章節(jié)會介紹一些 React 的基礎(chǔ)知識和基本用法。已經(jīng)入門 React 基礎(chǔ)的同學(xué)坡椒,可以簡單看看這篇文檔并略過視頻內(nèi)容扰路。React 零基礎(chǔ)的同學(xué)還建議去慕課網(wǎng)學(xué)習(xí)React入門基礎(chǔ)。
另外倔叼,本教程的代碼將全部使用 es6 語法汗唱,教程中我會介紹一些用到的 es6 語法,但是不會從頭講解了丈攒,推薦閱讀es6入門
hello world
以下是一個最簡單的demo哩罪,將一個最簡單的組件渲染到頁面上。
import React from 'react'
import { render } from 'react-dom'
// 定義組件
class Hello extends React.Component {
render() {
// return 里面寫jsx語法
return (
<p>hello world</p>
)
}
}
// 渲染組件到頁面
render(
<Hello/>,
document.getElementById('root')
)
深入一下巡验,這里import React from 'react'
引用的是什么际插?
這里的'react'
對應(yīng)的就是./package.json
文件中dependencies
中的'react'
,即在該目錄下用npm install
安裝的 react 显设。npm 安裝的 react 的物理文件是存放在 ./node_modules/react
中的框弛,因此引用的東西肯定就在這個文件夾里面。
打開./node_modules/react/package.json
找到"main": "react.js",
捕捂,這里的main
即指定了入口文件功咒,即./node_modules/react/react.js
這個文件。那么绞蹦,問題的答案自然就出來了。
jsx 語法
React 里面寫模板要使用 jsx 語法榜旦,它其實和 html 很相似但是又有那么幾點不一樣幽七。下面簡單介紹一下 jsx 語法的一些特點:
使用一個父節(jié)點包裹
jsx 中不能一次性返回零散的多個節(jié)點,如果有多個請包涵在一個節(jié)點中溅呢。例如澡屡,
// 三個 <p> 外面必須再包裹一層 <div>
return (
<div>
<p>段落1</p>
<p>段落2</p>
<p>段落3</p>
</div>
)
再例如:
// { } 中返回的兩個 <p> 也要用 <div> 包裹
return (
<div>
<p>段落1</p>
{
true
? <p>true</p>
: <div>
<p>false 1</p>
<p>false 2</p>
</div>
}
</div>
)
注釋
jsx 中用{/* */}
的注釋形式
return (
// jsx 外面的注釋
<div>
{/* jsx 里面的注釋 */}
<p>hello world</p>
</div>
)
樣式
對應(yīng) html 的兩種形式,jsx 的樣式可以這樣寫:
css樣式:<p className="class1">hello world</p>
咐旧,注意這里是className
驶鹉,而 html 中是class
內(nèi)聯(lián)樣式:<p style={{display: 'block', fontSize: '20px'}}>hello world</p>
,注意這里的{{...}}
铣墨,還有fontSize
的駝峰式寫法
事件
拿 click 事件為例室埋,要在標(biāo)簽上綁定 click 事件,可以這樣寫
class Hello extends React.Component {
render() {
return (
<p onClick={this.clickHandler.bind(this)}>hello world</p>
)
}
clickHandler(e) {
// e 即js中的事件對象伊约,例如 e.preventDefault()
// 函數(shù)執(zhí)行時 this 即組件本身姚淆,因為上面的 .bind(this)
console.log(Date.now())
}
}
注意,onClick
是駝峰式寫法屡律,以及.bind(this)
的作用
循環(huán)
在 jsx 中使用循環(huán)腌逢,一般會用到Array.prototype.map
(來自ES5標(biāo)準)
class Hello extends React.Component {
render() {
const arr = ['a', 'b', 'c']
return (
<div>
{arr.map((item, index) => {
return <p key={index}>this is {item}</p>
})}
</div>
)
}
}
注意,arr.map
是包裹在{}
中的超埋,key={index}
有助于React的渲染優(yōu)化搏讶,jsx中的{}
可放一個可執(zhí)行的 js 程序或者變量
判斷
jsx中使用判斷一般會用到三元表達式(表達式也是放在{}
中的)佳鳖,例如:
return (
<div>
<p>段落1</p>
{
true
? <p>true</p>
: <p>false</p>
</div>
}
</div>
)
也可以這樣使用:
<p style={{display: true ? 'block' ? 'none'}}>hello world</p>
代碼分離
之前的demo代碼都是在一個文件中,實際開發(fā)中不可能是這樣子的媒惕,因此這里就先把組件的代碼給拆分開系吩。我們將使用 es6 的模塊管理規(guī)范。
page 層
創(chuàng)建./app/containers/Hello/index.jsx
文件吓笙,將之前創(chuàng)建組件代碼復(fù)制進去
import React from 'react'
class Hello extends React.Component {
render() {
return (
<p>hello world</p>
)
}
}
export default Hello
然后./app/index.jsx
中代碼就可以這樣寫淑玫。
import Hello from './containers/Hello';
render(
<Hello/>,
document.getElementById('root')
)
注意,代碼import Hello from './containers/Hello';
這里可以寫成./containers/Hello/index.jsx
也可以寫成./containers/Hello/index
subpage 層
如果Hello
組件再稍微復(fù)雜一點面睛,那么把代碼都放一塊也會變得復(fù)雜絮蒿,接下來我們再拆分。
創(chuàng)建./app/containers/Hello/subpage
目錄叁鉴,然后在其下創(chuàng)建三個文件Carousel.jsx
Recommend.jsx
List.jsx
土涝,分別寫入相應(yīng)的代碼(看代碼文件即可),然后./app/containers/Hello/index.js
中即可這樣寫
import Carousel from './subpage/Carousel'
import Recommend from './subpage/Recommend'
import List from './subpage/List'
class Hello extends React.Component {
render() {
return (
<div>
<p>hello world</p>
<hr/>
<Carousel/>
<Recommend/>
<List/>
</div>
)
}
}
注意幌墓,這里import
時.jsx
后綴省略了但壮。
component 層
以上介紹的是頁面和復(fù)雜頁面的拆分,但那都是頁面層級的常侣,即page
層蜡饵。這里復(fù)雜頁面拆分為subpage
其實沒啥特別的,就是把復(fù)雜頁面的代碼拆分一下胳施,會更加符合開放封閉原則溯祸。而且,只有復(fù)雜頁面才有必要去拆分舞肆,簡單頁面根本沒必要拆分焦辅。因此,無論是page
還是subpage
它都是頁面級別的椿胯。
頁面的特點是其獨特性筷登,一個頁面就需要創(chuàng)建一個文件(如果兩個頁面可以共用一個文件,這是設(shè)計不合理哩盲,得治)前方。而頁面其中的內(nèi)容,就不一定是這樣子了廉油。例如镣丑,現(xiàn)在的APP每個頁面最上面都會有個 header ,即可以顯示標(biāo)題娱两,可以返回莺匠。每個頁面都有,樣子差不多十兢,難道我們要為每個頁面都做一個趣竣?——當(dāng)然不是摇庙。
創(chuàng)建./app/components/Header/index.jsx
文件,簡單寫入一個組件的代碼(見源碼文件)遥缕,然后在./app/containers/index.jsx
中引用
import Header from '../../components/Header'
class Hello extends React.Component {
render() {
return (
<div>
<Header/>
{/* 省略其他內(nèi)容 */}
</div>
)
}
}
Hello 頁面會用到 Header卫袒,以后的其他頁面也會用到 Header ,我們把多個頁面都可能用到的功能单匣,封裝到一個組件中夕凝,代碼放在./app/components
下。
數(shù)據(jù)傳遞 & 數(shù)據(jù)變化
props
接著剛才 Header 的話題往下說户秤,每個頁面都會使用 Header 码秉,但是 Header 上顯示的標(biāo)題每個頁面肯定是不一樣的。我們需要這樣解決:頁面中引用Header時鸡号,這樣寫 <Header title="Hello頁面"/>
转砖,即給 Header 組件設(shè)置一個 title 屬性。而在 Header 組件中可以這樣取到
render() {
return (
<p>{this.props.title}</p>
)
}
在 React 中鲸伴,父組件給子組件傳遞數(shù)據(jù)時府蔗,就是以上方式,通過給子組件設(shè)置 props 的方式汞窗,子組件取得 props 中的值即可完成數(shù)據(jù)傳遞姓赤。被傳遞數(shù)據(jù)的格式可以是任何 js 可識別的數(shù)據(jù)結(jié)構(gòu),上面demo是一個字符串仲吏。React 中模捂,props 一般只作為父組件給子組件傳遞數(shù)據(jù)用,不要試圖去修改自己的 props 蜘矢,除非你想自找麻煩
props && state
上面提到了 props 不能被自身修改,如果組件內(nèi)部自身的屬性發(fā)生變化综看,該怎么辦品腹?—— React 為我們提供給了 state
,先看一個demo:
class Hello extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
// 顯示當(dāng)前時間
now: Date.now()
}
}
render() {
return (
<div>
<p>hello world {this.state.now}</p>
</div>
)
}
}
還有一點非常重要红碑,React 會實時監(jiān)聽每個組件的 props 和 state 的值舞吭,一旦有變化,會立刻更新組件析珊,將結(jié)果重新渲染到頁面上羡鸥,下面demo演示了state
的變化,props
也是一樣的
class Hello extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
// 顯示當(dāng)前時間
now: Date.now()
}
}
render() {
return (
<div>
<p onClick={this.clickHandler.bind(this)}>hello world {this.state.now}</p>
</div>
)
}
clickHandler() {
// 設(shè)置 state 的值的時候忠寻,一定要用 this.setState 惧浴,不能直接賦值修改
this.setState({
now: Date.now()
})
}
}
智能組件 & 木偶組件
這是用 React 做系統(tǒng)設(shè)計時的兩個非常重要的概念。雖然在 React 中奕剃,所有的單位都叫做“組件”衷旅,但是通過以上例子捐腿,我們還是將它們分別放在了./app/containers
和./app/components
兩個文件夾中。為何要分開呢柿顶?
- 智能組件 在日常開發(fā)中茄袖,我們也簡稱“頁面”。為何說它“智能”嘁锯,因為它只會做一些很聰明的事兒宪祥,臟活累活都不干。它只對數(shù)據(jù)負責(zé)家乘,只需要獲取了數(shù)據(jù)蝗羊、定義好數(shù)據(jù)操作的相關(guān)函數(shù),然后將這些數(shù)據(jù)烤低、函數(shù)直接傳遞給具體實現(xiàn)的組件即可肘交。
- 木偶組件 這里“木偶”一詞用的特別形象,它總是被人拿線牽著扑馁。它從智能組件(或頁面)那里接受到數(shù)據(jù)涯呻、函數(shù),然后就開始做一些展示工作腻要,它的工作就是把拿到的數(shù)據(jù)展示給用戶复罐,函數(shù)操作開放給用戶。至于數(shù)據(jù)內(nèi)容是什么雄家,函數(shù)操作是什么效诅,它不關(guān)心。
以上兩個如果不是理解的很深刻趟济,待把課程學(xué)完再回頭看一下這兩句話乱投,相信會理解的。
生命周期
React 詳細的生命周期可參見這里顷编,也可查閱本文檔一開始的視頻教程戚炫。這里我們重點介紹這個項目開發(fā)中常用的幾個生命周期函數(shù)(hook),相信你在接下來的 React 開發(fā)中媳纬,也會常用這些双肤。
以下聲明周期,也沒必要每個都寫demo來解釋钮惠,先簡單了解一下茅糜,后面會根據(jù)實際的例子來解釋,這樣會更加易懂素挽。
getInitialState
初始化組件 state 數(shù)據(jù)蔑赘,但是在 es6 的語法中,我們可以使用以下書寫方式代替
class Hello extends React.Component {
constructor(props, context) {
super(props, context);
// 初始化組件 state 數(shù)據(jù)
this.state = {
now: Date.now()
}
}
}
render
最常用的hook,返回組件要渲染的模板米死。
comopentDidMount
組件第一次加載時渲染完成的事件锌历,一般在此獲取網(wǎng)絡(luò)數(shù)據(jù)。實際開始項目開發(fā)時峦筒,會經(jīng)常用到晓褪。
shouldComponentUpdate
主要用于性能優(yōu)化芜繁,React 的性能優(yōu)化也是一個很重要的話題,后面一并講解。
componentDidUpdate
組件更新了之后觸發(fā)的事件哟玷,一般用于清空并更新數(shù)據(jù)茬底。實際開始項目開發(fā)時坯沪,會經(jīng)常用到筒严。
componentWillUnmount
組件在銷毀之前觸發(fā)的事件,一般用戶存儲一些特殊信息尉辑,以及清理setTimeout
事件等帆精。