每一個的地方,每一種的知識,每一種事物,都是從陌生到熟悉。在這個過程裡面,或許能開闊眼界,增長見識,體驗樂趣。一切都歸於我們的心態與行動。
1.前言
學習之路不可停止,最近在玩 React。也動手嘗試寫了一個例項。藉此整理總結下初步學習 React 的一些基礎知識。因為這幾天比較忙,沒那麼多時間,所以例項和文章沒有很細緻。如果大家發現文章有錯誤的地方,請多諒解。有什麼更新的建議,歡迎在評論區指出。該文章主要是大概講下和快速上手使用,如果深入,要靠自己探索,後期我也會補充文章。
1.這裡使用 React 版本是 16.4
2.如果大家看的有點蒙圈,可能要去 React 上面看下 React 的基礎。
3.程式碼已經上傳到 Github 上面,需要的可以去進行下載,歡迎 star react-demos。
4.建議大家看該文章的時候也開啟編輯器,邊寫邊看,思路會清晰很多
2.預熱知識
學習React之前,一定要對下面兩個知識點有所瞭解。如果不瞭解下面的知識,請前往下面對應的連結,進行學習。
2-1.JSX
學習 React 例項之前,一定要對JSX有一定的瞭解。JSX 可以說是一個語法糖,React 使用來替代常規的 JavaScript。看起來很像 XML 的 JavaScript 語法擴充套件。
JSX 不是必須的,如下兩段程式碼,是完全等價的,但是使用JSX會更加的清晰,簡潔,易懂。 JSX
let el=<div className="author">守候</div>
ReactDOM.render(
el,
document.getElementById('example')
);
複製程式碼
HTML
let el=React.createElement('div',{className:'suthor'},'守候')
ReactDOM.render(
el,
document.getElementById('example')
);
複製程式碼
關於 JSX 語法的更多內容,大家請看 React JSX 。這裡不做過多的介紹。
2-2.元件
元件是 React 最重要的一個概念。比如下面的程式碼,就可以說是一個元件。
class Author extends React.Component{
render(){
return (
<div className="author">守候</div>
)
}
}
複製程式碼
當然也可以使用函式元件的方式定義。
function Author(props) {
return <div className='author'>守候</div>;
}
複製程式碼
把元件掛在到頁面的 id 為 example 的一個 Dom 元素上面。
ReactDOM.render(
<Author />,
document.getElementById('example')
);
複製程式碼
上面這裡,就是一個元件,這個元件只有一個元素。元件也可以由多個元素組成
class Author extends React.Component{
render(){
return (
<div className="author">
<span class="author-name">守候</span>
<span class="author-gener">男</span>
</div>
)
}
}
複製程式碼
參考連結:React 元件。
3.例項
這個例子的執行效果如下,是一個非常簡單的例項。下面通過這個例項,接觸下 React 的一些基礎知識和使用方式。
3-1.渲染
渲染其實上面例子就已經有了,就是利用 render 函式返回一個元件。之後利用 ReactDOM.render 進行掛載到頁面上的特定 Dom 元素裡面。
class EquipmentList extends React.Component{
render(){
return (
<div>
<a href="javascript:;">殲擊機</a>
<a href="javascript:;">轟炸機</a>
<a href="javascript:;">運輸機</a>
<ul>
<li>殲20</li>
<li>轟6K</li>
<li>運20</li>
</ul>
</div>
)
}
}
//掛載到頁面裡 id 為 example 的元素上面
ReactDOM.render(
<EquipmentList />,
document.getElementById('example')
);
複製程式碼
3-2.狀態和迴圈
上面是最基本的一個頁面佈局,但是頁面是靜態的,不是根據資料渲染的。下面認識下狀態和迴圈,以資料驅動渲染。
首先來說下狀態
引用菜鳥教程的說法:React 把元件看成是一個狀態機(State Machines)。通過與使用者的互動,實現不同狀態,然後渲染 UI,讓使用者介面和資料保持一致。React 裡,只需更新元件的 state,然後根據新的 state 重新渲染使用者介面(不要操作 DOM)。
我們把上面的程式碼,稍微改下,給元件加上狀態。
class EquipmentList extends React.Component{
constructor(){
super();
this.state={
post1:'殲20',
post2:'轟6K',
post3:'運20',
}
}
render(){
return (
<div>
<a href="javascript:;">殲擊機</a>
<a href="javascript:;">轟炸機</a>
<a href="javascript:;">運輸機</a>
<ul>
<li>{this.state.post1}</li>
<li>{this.state.post2}</li>
<li>{this.state.post3}</li>
</ul>
</div>
)
}
}
ReactDOM.render(
<EquipmentList />,
document.getElementById('example')
);
複製程式碼
這裡有3個知識點需要知道
1.super(); 這個一定要呼叫,這裡相當於呼叫了 React.Component 的 constructor。目的就是初始化 React 元件。
2.this.state 就是元件的狀態
3.render 方法裡面,輸入的是 state (也可以是 props)。輸出的就是元件。
頁面效果完全一樣,至於狀態的修改等,下面再例項再提及。
但是這樣寫程式碼顯然是不優雅的,如果資料一多,工作量就很大,就應該使用迴圈進行渲染。下面把程式碼改下。
class EquipmentList extends React.Component{
constructor(){
super();
this.state={
equipmentList:[
{
id:1,
title:'殲20'
},
{
id:2,
title:'轟6K'
},
{
id:3,
title:'運20'
}
],
}
}
render(){
return (
<div>
<a href="javascript:;">殲擊機</a>
<a href="javascript:;">轟炸機</a>
<a href="javascript:;">運輸機</a>
<ul>
{this.state.equipmentList.map(item=><li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
}
ReactDOM.render(
<EquipmentList />,
document.getElementById('example')
);
複製程式碼
可能大家會有疑問,為什麼li要帶上 key 這個屬性。是因為 React 是使用 key 屬性來標誌列表中的所有元素,當列表資料發生變化時,React 通過 key 可以更快的知道哪些元素髮生了變化,從而只重新渲染髮生變化的元素,提高效率和效能。在列表裡面 key 需要唯一,一般是使用 id 作為 key 值,不建議使用 index 作為 key 值。因為如果列表發生了刪除,插入等操作,列表要重排。index 值會改變,可能會影響渲染的效率和效能。
3-3.事件
接下來就給元件新增時間,點選 a 元素的時候,比如點選‘殲擊機’,就應該顯示 equipmentList 裡面 title 等於‘殲20’的資料,點選‘轟炸機’,就應該顯示 equipmentList 裡面 title 等於‘轟6K’的資料......
實現這個需求,其實很簡單,就是新建一個狀態 equipmentListNow ,元件裡面只遍歷 equipmentListNow。點選‘殲擊機’,就把 equipmentList 裡面 title ‘殲20’的資料賦值給postListNow,點選‘轟炸機’,就把 equipmentList 裡面 title 等於‘轟6K’的資料賦值給postListNow......。
下面簡單實現一下
class EquipmentList extends React.Component{
constructor(){
super();
this.state={
equipmentList:[
{
id:1,
title:'殲20'
},
{
id:2,
title:'轟6K'
},
{
id:3,
title:'運20'
}
],
equipmentListNow:[]
}
}
switchTab(id){
let _list=this.state.equipmentList.filter(item=>item.id===id);
this.setState({
equipmentListNow:_list
})
}
render(){
return (
<div>
<a href="javascript:;" onClick={()=>{
this.switchTab(1);
}}>殲擊機</a>
<a href="javascript:;" onClick={()=>{
this.switchTab(2);
}}>轟炸機</a>
<a href="javascript:;" onClick={()=>{
this.switchTab(3);
}}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
}
ReactDOM.render(
<EquipmentList />,
document.getElementById('example')
);
複製程式碼
如果要修改 state ,只能使用如: this.setState({equipmentListNow:_list}) 改變。切記不能出現如下寫法:this.equipmentListNow=_list;
跑起來了,沒報錯,但是頁面上一開始列表沒有被渲染出來,要點選的時候才有特定的資料出來(點選第一章的a,出現第一章的資料,以此類推)。原因想必大家也知道,因為在頁面初始化的時候,equipmentListNow 只是一個空陣列,所以正確的做法是在頁面初始化的時候,就把 equipmentList 的值賦給 equipmentListNow。頁面初始化賦值,就是下面元件生命週期的內容了。
3-4.生命週期
為了讓頁面載入後,equipmentListNow 能有初始化的內容,那麼需要在生命週期函式裡面把 equipmentList 的值賦給 equipmentListNow。
至於生命週期,這裡不展開講,大家可以看下文件:React 元件生命週期。
大家應該知道,這個初始化賦值操作,應該在渲染前就完成。如果在渲染後再操作,那麼就相當於頁面渲染了第二次。所以我們現在用到的生命週期函式是 componentWillMount 。
class EquipmentList extends React.Component{
constructor(){
super();
this.state={
equipmentList:[
{
id:1,
title:'殲擊機'
},
{
id:2,
title:'轟炸機'
},
{
id:3,
title:'運輸機'
}
],
equipmentListNow:[]
}
}
switchTab(id){
let _list=this.state.equipmentList.filter(item=>item.id===id);
this.setState({
equipmentListNow:_list
})
}
componentDidMount(){
let _list=this.state.equipmentList;
this.setState({
equipmentListNow:_list
})
}
render(){
return (
<div>
<a href="javascript:;" onClick={()=>{
this.switchTab(1);
}}>殲擊機</a>
<a href="javascript:;" onClick={()=>{
this.switchTab(2);
}}>轟炸機</a>
<a href="javascript:;" onClick={()=>{
this.switchTab(3);
}}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
}
ReactDOM.render(
<EquipmentList />,
document.getElementById('example')
);
複製程式碼
這樣一來,equipmentListNow 在頁面渲染前就初始化有值了,頁面也正常了。
3-5.元件樣式
上面的元件,一行 CSS 都沒寫,看著就特別難看。下面就新增些樣式。
方式1:最簡單的方式就是,就是給元件起 class 。在外部寫上 CSS 樣式。
CSS 程式碼
body{
font-family: "微軟雅黑";
}
ul{
margin: 0;
padding: 0;
}
.post-box{
width: 600px;
margin: 30px auto;
}
.post-box a{
display: inline-block;
font-size: 14px;
margin-right: 10px;
width: 80px;
height: 30px;
line-height: 30px;
text-align: center;
background: #09f;
color: #fff;
text-decoration: none;
}
.post-box li{
list-style-type: none;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #ccc;
}
複製程式碼
JS 程式碼
在 js class 是關鍵字,所以要給元素設定 class 屬性,需要用 className
render() {
return (
{/*給div增加class*/}
<div className="post-box">
...
</div>
)
}
複製程式碼
方式2:由於 React 的機制,所以很多時候會使用 css-in-js 這種方式,設定元素的樣式,簡單來說就是設定元素的內聯樣式。
render() {
return (
<div className="post-box">
{/*給a設定內聯樣式*/}
<a href="javascript:;" onClick={() => {
this.switchTab(1);
}} style={{background:'#f90'}}>殲擊機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(2);
}} style={{background:'#f00'}}>轟炸機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(3);
}} style={{background:'#0f0'}}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
複製程式碼
css-in-js 的另一種寫法是:宣告樣式變數
let _style={
background:'#09f',
color:'#f3f201'
}
複製程式碼
render() {
return (
<div className="post-box">
{/*給a設定內聯樣式*/}
<a href="javascript:;" onClick={() => {
this.switchTab(1);
}} style={_style}>殲擊機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(2);
}} style={_style}>轟炸機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(3);
}} style={_style}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
複製程式碼
3-6.有狀態元件和無狀態元件
大家可以看到,上面的元件,是有一個狀態 state 的。但大家看了文章開始的例子就知道,並不是所有的元件都是需要 state 的。根據有無 state 。可以把元件區分為有狀態元件和無狀態元件。把有狀態元件和無狀態元件合理利用,分工合作,可以說是用好 React 的第一步,下面簡單分析下。
不難發現,上面 EquipmentList 元件複用性不強。想要複用,必須把程式碼拷貝過去,然後再修改 equipmentList 這個狀態。
大家應該知道 EquipmentList 要想複用,裡面的陣列不能寫死,只能由外部傳入,EquipmentList 通過 props 獲取資料。
既然說到了 props 就順便提下,props 的作用就是把父元件的值傳給子元件。props 是一個物件。如下
下面引用 2-2 的一個例子。
ReactDOM.render(
<Author name='守候' gender='男'/>,
document.getElementById('example')
);
複製程式碼
那麼 Author 裡面收到的 props 就是
props={
name:'守候',
gender:'男'
}
複製程式碼
使用方式也很簡單
class Author extends React.Component{
render(){
return (
<div className="author">
<span class="author-name">{this.props.name}</span>
<span class="author-gender">{this.props.gender}</span>
</div>
)
}
}
複製程式碼
引用菜鳥教程說法:state 和 props 主要的區別在於 props 是不可變的,而 state 可以根據與使用者互動來改變。這就是為什麼有些容器元件需要定義 state 來更新和修改資料。 而子元件只能通過 props 來傳遞資料。
說了這麼多,下面修改下,把 EquipmentList 封裝成一個能複用的元件。
class EquipmentList extends React.Component {
constructor(props) {
super(props);
this.state = {
//根據props獲取資料
equipmentList: this.props.list,
equipmentListNow: []
}
}
switchTab(id) {
let _list = this.state.equipmentList.filter(item => item.id === id);
this.setState({
equipmentListNow: _list
})
}
componentDidMount() {
let _list = this.state.equipmentList;
this.setState({
equipmentListNow: _list
})
}
render() {
return (
<div className='post-box'>
<a href="javascript:;" onClick={() => {
this.switchTab(1);
}}>殲擊機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(2);
}}>轟炸機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(3);
}}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
}
複製程式碼
這裡有一個要注意的點 在 constructor 裡面,我寫的是 super(props) 不是之前那樣的 super() 。這樣的寫的原因就是為了在 constructor 裡面可以使用 this.props。如下程式碼
constructor(props) {
super(props);
this.state = {
//根據props獲取資料
equipmentList: this.props.list,
equipmentListNow: []
}
}
複製程式碼
如不需要用 this.props 。可以直接寫 super(),當然寫 super(props) 也沒錯。
constructor(props) {
super();
this.state = {
//根據props獲取資料
equipmentList: props.list,
equipmentListNow: []
}
}
複製程式碼
說了這麼多,下面看下怎麼使用。
使用方式1,這裡只做一個說明,這樣子寫實際是沒什麼意思的。
let equipmentList = [
{
id: 1,
title: '殲20'
},
{
id: 2,
title: '轟6K'
},
{
id: 3,
title: '運20'
}
]
ReactDOM.render(
<EquipmentList list={equipmentList}/>,
document.getElementById('example')
);
複製程式碼
使用方式2
class Example2 extends React.Component {
constructor(props) {
super(props);
this.state = {
equipmentList: [
{
id: 1,
title: '殲20-量產型號'
},
{
id: 2,
title: '轟6K-量產型號'
},
{
id: 3,
title: '運20-量產型號'
}
],
}
}
componentDidMount() {
let _list = this.state.equipmentList;
//兩秒後更新資料
setTimeout(() => {
_list.forEach((item,index)=>{
item.title='其實這是一艘航空母艦'+index;
});
this.setState({
equipmentList: _list
})
}, 2000)
}
render(){
return (
<EquipmentList list={this.state.equipmentList}></EquipmentList>
)
}
}
ReactDOM.render(<Example2/>,
document.getElementById('example2')
);
複製程式碼
這樣一來,EquipmentList 變成了無狀態元件,Example2 變成了有狀態元件。分工方面,EquipmentList 不運算元據的變化,只管資料的渲染;Example2 不關注如何渲染,只控制資料變化,每次變化,使用 setState 更新資料,EquipmentList 的渲染結果就會改變。
看到這可能大家也有感悟了,一般而言,一個頁面上絕大部分應該是無狀態元件,少部分是有狀態元件。
最後提一點,可能大家也有會疑問,如果 props 很多都是重複的值,可不可以設定一個預設值,就可以少傳些引數。答案是可以的,比如下面程式碼,掛載的時候 EquipmentList 沒有傳值,但是能渲染出來,因為使用了 defaultProps 設定 props 的預設值。
class EquipmentList extends React.Component {
constructor(props) {
super(props);
this.state = {
//根據props獲取資料
equipmentList: this.props.list,
equipmentListNow: []
}
}
switchTab(id) {
let _list = this.state.equipmentList.filter(item => item.id === id);
this.setState({
equipmentListNow: _list
})
}
componentDidMount() {
let _list = this.state.equipmentList;
this.setState({
equipmentListNow: _list
})
}
render() {
return (
<div>
<a href="javascript:;" onClick={() => {
this.switchTab(1);
}}>殲擊機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(2);
}}>轟炸機</a>
<a href="javascript:;" onClick={() => {
this.switchTab(3);
}}>運輸機</a>
<ul>
{this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
</div>
)
}
}
//設定 props 預設值
EquipmentList.defaultProps={
list:[
{
id: 1,
title: '殲20'
},
{
id: 2,
title: '轟6K'
},
{
id: 3,
title: '運20'
}
]
}
ReactDOM.render(<EquipmentList/>,
document.getElementById('example')
);
複製程式碼
4.小結
好了,這幾天對 React 學習的一些總結,就暫時告一段落了。該文章只是針對 React 實現一個非常簡單的例項,也很基礎。如果要深入,就要大家各自去努力了,在往後深入學習裡面,我也會繼續寫文章,分享。希望和大家有更多的交流,如果大家對文章有什麼看法和建議,歡迎指點。
-------------------------華麗的分割線--------------------
想了解更多,和我交流,內推職位,請新增我微信。或者關注我的微信公眾號:守候書閣