目錄
2_用命令create-react-app (腳手架)創建項目目錄demo1
this.state:react中用來控制組件內部狀態的數據
9_jsx中幾個要注意的地方:代碼註釋、css的class陷阱、html解析問題dangerouslySetInnerHTML屬性、label標籤的for
3.html解析問題的:dangerouslySetInnerHTML屬性
10_react快速生成代碼插件:Simple React Snippets
2.子組件操作父組件數據(實際上是父組件向子組件傳方法):在子組件加上屬性的形式。
①父組件向子組件傳值 、子組件向父組件傳值(父組件向子組件傳方法)都是在子組件加上屬性的形式實現的,然後通過this.props.屬性名的方式訪問。
React中,使用外部(被傳入傳入的)數據(通過 this.props 訪問),被傳入的數據可在組件中通過 this.props 在 render() 訪問。
組件還可以維護其內部的有狀態的數據(通過 this.state 訪問)。
②調用方法時要綁定this指向,可以直接在調用處bind綁定,但是更好的是在組件構造函數內綁定,這樣有助於性能優化。
14_react developer tools的安裝與使用
2.react developer tools插件的三種狀態
由於本學習筆記過長,圖片較多,編輯器打字時有些卡頓,所以之後內容寫在另一篇博客:
1_開發環境搭建
①前往http://nodejs.cn/,找到自己電腦適合的版本下載並安裝node.js。
②win鍵+r(運行),cmd打開命令行。
輸入相關命令檢查node.js是否安裝成功,顯示nodejs與npm對應的版本即爲安裝成功。
安裝node主要爲了使用npm包管理工具。
③安裝官方腳手架工具create-react-app。
若無法安裝create-react-app(安裝失敗),
可嘗試更換網絡環境後重新安裝,若還是失敗,排除網絡因素後,則以管理員身份運行cmd(命令提示符)後成功安裝create-react-app
看到安裝了91個包就表示安裝成功。
2_用命令create-react-app (腳手架)創建項目目錄demo1
創建失敗:
解決方法:
同樣以管理員身份運行cmd(命令提示符),重新輸入命令create-react-app 創建項目demo1
成功用create-react-app創建demo1,可以看到demo1裏面有許多腳手架生成的文件。
根據命令提示運行我的第一個react文件
3_通過組件化開發輸出helloworld
把src目錄裏面的文件全部刪掉,然後新建index.js(入口文件)與App.js(方法組件,React核心之一就是組件化開發)
入口文件index.js代碼如下(用ReactDOM的render渲染語法把APP模塊渲染到public\index.html中的root上):
import React from 'react' //要用react所以引入react
import ReactDOM from 'react-dom' //要操作dom所以引入react-dom
import App from './App' //組件化開發,有了App組件後就可以通過reactdom的render方法在public\index.html的rootID上渲染出來
ReactDOM.render(<App />,document.getElementById('root')) //用react語法把App組件render到index.html中id爲root的標籤上
注意此時index.js中的三句導入都是導入默認組件,React、ReactDOM、App這三個組件名都是自己取得,取ABC也可以,但是爲了做到望文生義,以固定寫法比較好。其中React、ReactDOM是導入默認組件,App是導入的自定義組件(必須以大寫字母開頭)。
組件App.js代碼如下(React中自定義組件名必須以大寫字母開頭,例如本例的App組件必須大寫字母開頭,不然會報錯):
import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
class App extends Component{
render(){
return (
<div>
Hello,EasyLee
<ul className='my_list'>
<li>{true ? 'i love coding' : 'i love eating' }</li>
<li>i love react</li>
</ul>
{/* 上述jsx代碼相當於如下js代碼:
var child1 = React.createElement('li',null,'i love coding');
var chidl2 = React.createElement('li',null,'i love react');
var chidl3 = React.createElement('ul',{className='my_list'},child1,child2); */}
</div>
)
}
}
export default App;//在index.js文件中要引用App組件所以最後要用export default暴露(輸出)App組件。
在index.js文件中要引用App組件所以最後要用export default暴露(輸出)App組件。
疑問:可以把import React,{Component} from 'react'中的Component改成a嗎?
答:不能,Component是react的成員組件,react官方自己定義好的,所以不能鎖邊取一個名字。
public目錄下模板文件index.html代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>EasyLee WEB</title>
</head>
<body>
<noscript>需要開啓JavaScript.</noscript>
<div id="root"></div>
</body>
</html>
有幾點(①②③④)要注意:
①入口文件index.js與組件App.js導入的頭部文件不同,:
入口文件index.js頭部通常由三部分組成:導入react默認組件、導入ReactDOM、導入自定義組件。
②入口文件index.js與組件App.js導入的底部代碼不同:
入口文件index.js下方把自定義組件(以App爲例)通過ReactDOM.render渲染到index.html文件上。
自定義組件App.js底部要通過export default 語句導出。
index.js(入口文件):
import React from "react"//①要用react所以引入react
import ReactDOM from "react-dom"//②要操作dom所以引入react-dom
import App from "./App"//③導入自定義組件
ReactDOM.render(<App />,document.getElementById('root'))
App.js(自定義組件以App.js爲例,底部要通過export default 語句導出):
import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
export default App;//底部導出自定義組件
③自定義組件名首字母必須大寫,如App。
④牢記自定義組件的語法結構,以App.js組件爲例:
import React,{Component} from "react"
class App extends Component{
render(){
return (
<div>
</div>
)
}
}
export default App;//底部導出自定義組件
4_JSX語法
JSX就是Javascript和XML結合的一種格式。React發明了JSX,可以方便的利用HTML語法來創建虛擬DOM,當遇到<
,JSX就當作HTML解析,遇到{
就當JavaScript解析.
import React,{Component} from 'react'
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
class App extends Component{
render(){
return (
<div>
{/* Hello,EasyLee! */}
<ul className='my_list'>
<li>{true ? 'i love coding' : 'i love eating' }</li>
<li>i love react</li>
</ul>
</div>
)
// 上述jsx代碼相當於如下js代碼:
// var child1 = React.createElement('li',null,'i love coding');
// var chidl2 = React.createElement('li',null,'i love react');
// var chidl3 = React.createElement('ul',{className='my_list'},child1,child2);
}
}
export default App;
其中 createElement(a, b, c)方法用法如下:
①參數 a:表示元素的類型,比如:h1, div 等。
②參數 b:表示該元素上的屬性,使用 JavaScript 對象方式表示。
③參數 c:表示該元素內部的內容(子元素),可以是文字,可以繼續嵌套另外一個 React.createElement(a, b, c)
。
這種方法其實在實際 React 開發中幾乎不會使用,因爲可以直接用JSX語法 。
5_小姐姐服務頁面(Xiaojiejie.js)編寫
疑問:爲何每個自定義組件都要引入React?
答:因爲webpack在解析jsx的時候會把下面代碼的Component解析成React.Component這個方法,假如他發現沒有引入React,就會報錯。
Xiaojiejie.js代碼如下:
import React,{Component} from 'react'
class Xiaojiejie extends Component{
render(){
return (
<div>
<input /><button>增加服務</button>
<ul>
<li>服務1</li>
<li>服務2</li>
</ul>
</div>
)
}
}
export default Xiaojiejie;
在index.js中加入如下代碼:
ReactDOM.render(<Xiaojiejie />,document.getElementById('xiaojiejie'))
在index.html中加入如下代碼:
<div id="xiaojiejie"></div>
加入Xiaojiejie.js後運行效果如圖所示:
問:由於React要求必須在一個組件的最外層進行包裹,如下圖的光標處的div,不加就會報錯,但是在Flex佈局中,return內最外層包裹div會影響佈局,Flex佈局中最外層不能有包裹,這時候怎麼辦呢?
答:Flex佈局中<div>由<Fragment>替代,不過記得要在頭部多引入react的成員組件Fragment(注意成員組件Fragment的首字母是大寫的,引入和使用Fragment標籤時要注意):
import React,{Component,Fragment } from 'react'
class Xiaojiejie extends Component{
render(){
return(
<Fragment> {/*注意Fragment標籤首字母大寫*/}
<div>
<input />
<button>增加服務</button>
</div>
<ul>
<li>服務1</li>
<li>服務2</li>
</ul>
</Fragment>
)
}
}
6_React響應式設計原理與數據的綁定方法、增加服務項
React如此受歡迎的原因(React響應式設計原理):
React不建議直接操作dom,而是讓你通過數據的改變從而自動幫你完成界面的改變,所以程序員無需關心dom操作只關心數據操作即可,大大加快了開發速度。
需求Z:
點擊增加服務按鈕就能在下方列表自動增加小姐姐的服務項(爲了實現需求Z,需求Z又拆分爲步驟ABCD,在小節6_中還沒完全實現需求Z,待小節7_中完善)。
實現Z:
{步驟A(分爲①②):在Xiaojiejie組件中添加構造函數,然後在構造函數constructor內定義數據。
①構造函數constructor(一般是固定寫法必須背下來, =:等標點符號要注意別寫錯):
constructor(props){
super(props)//調用父類方法(固定寫法)
this.state={
inputValue : '233',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
list:[]//服務列表
}
}
this.state:react中用來控制組件內部狀態的數據
②數據綁定(React中的數據綁定採用字面量{ }的形式,上文提到jsx就是遇到{ }當js代碼解析、遇到<>當html解析,{ }其實就是在jsx中使用js代碼時做的聲明):
<input value={this.state.inputValue} />
需求A結束}
由於我們在①中強行將inputValue 的數據綁定爲233是寫死的,所以在文本框中輸入任何東西都是沒反應的,如圖所示:
而且打開控制檯發現報錯,而且鍵盤輸入控制檯都沒反應,因爲已經報錯了:
控制檯提示:錯誤可設置onChange屬性,這個錯誤可以設置onChange值使控制檯恢復正常。
{需求B:改正控制檯的報錯
需求B的實現:爲input標籤設置onChange事件,綁定響應事件inputChange解決控制檯報錯(點擊查看onChange事件用法)
<input value={this.state.inputValue} onChange={this.inputChange} /> {/* input的onChange屬性:狀態值變化 */}
爲input標籤嘉善onChange事件後,控制檯就不報錯了。
onchange 事件:
①定義和用法:onchange 事件會在域的內容改變時發生
②語法:οnchange="SomeJavaScriptCode"
需求B結束}
{需求C:鍵盤輸入能改變inputValue的值(改變文本框中的數據),而不是寫死的233佔據文本框的位置
需求C的實現(分爲步驟C1.C2):
步驟C1:首先讓鍵盤輸入能輸出到控制檯中,
在render函數下方,加入響應事件inputChange(),並把onChange屬性綁定新寫的響應事件inputChange(),讓控制檯能輸出數據
inputChange(e){ //綁定響應事件改變文本框inputValue的值
console.log(e);
}
此時在文本框中輸入時控制檯就有反應了,在控制檯中會輸出很多內容了,這些內容就是e,如下面兩張圖所示:
此時響應事件能用了,那如何獲取從鍵盤輸入的值?
{步驟C2:通過e.target.value獲取從鍵盤輸入的值(注意e.target.value就是inputValue的值,即e.target.value等於寫死的233+鍵盤鍵入值)
inputChange(e){ //綁定響應事件改變文本框inputValue的值
console.log(e.target.value);//15_通過e.target.value打印文本框內的值
}
此時控制檯打印出文本框的值,e.target.value就是233加上鍵入的值(如圖所示):
此時想當然的會認爲只要將e.target.value的值賦給this.state.inputValue就完事了,就能去掉寫死的數據233了,但是這樣寫是錯誤的,控制檯報錯:
inputChange(e){ //綁定響應事件改變文本框inputValue的值
console.log(e.target.value);//15_通過e.target.value打印文本框內的值
this.state.inputValue=e.target.value;//錯誤寫法:錯誤①this指向錯誤②沒用this.setState方法
}
this.state.inputValue=e.target.value這種寫法犯了兩個錯誤(①②):
①this指向不正確,所以用bind重新綁定指向(ES5語法):
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/}
參考資料:
②React中,改變組件內部的狀態數據的值不能想當然的 this.state.inputValue=e.target.value(錯誤寫法)這樣寫,需要用到this.setState()方法:
inputChange(e){ //綁定響應事件改變文本框inputValue的值
// console.log(e);
// console.log(e.target.value);//15_通過e.target.value打印文本框內的值
// this.state.inputValue=e.target.value;//錯誤寫法:錯誤①this指向錯誤②沒用this.setState方法
this.setState({ //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
inputValue:e.target.value
})
}
此時在原本固定的inputValue爲233的文本框中輸入可改變文字了,如圖所示:
步驟C2結束}
步驟C結束}
7_增加服務項
{需求D:完善構造函數,讓render中寫死的列表數據化、增加服務項。
需求D的實現(①②):
①讓列表數據化
先在構造函數內爲list數組增加兩個數組元素,代碼如下:
constructor(props){
super(props)//調用父類方法(固定寫法)
this.state = {
inputValue:'233',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
}
}
構造函數中有了數據之後,用JavaScript Array map() 方法和箭頭函數循環輸出列表數據,代碼如下:
render(){
return(
<Fragment> {/*注意Fragment標籤首字母大寫*/}
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/}
<button>增加服務</button>
</div>
<ul>
{/* <li>服務1</li>
<li>服務2</li> */}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return <li>{item}</li>
})
}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
</ul>
</Fragment>
)
}
箭頭函數中有兩個參數:
一.item是循環的每一個子項
二.index是索引
服務列表動態循環輸出了,實現了列表數據化,但是發現控制檯報錯(一會解決):
報錯意思是list要有一個key值,暫用index+item來實現:
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return <li key={item+index}>{item}</li>
})
}
</ul>
報錯解決。
點擊:查看JavaScript Array map方法(es6)、箭頭函數用法。
一個好的經驗法則是:在 map()
方法中的元素需要設置 key 屬性(https://zh-hans.reactjs.org/docs/lists-and-keys.html#gatsby-focus-wrapper)
疑問:爲什麼map方法內第一個參數就是當前元素的值,第二個參數就是索引呢?
答:詳見Array.map方法的用法
②增加服務項
在增加按鈕上先綁定一個新寫的addList方法,
<button onClick={this.addList.bind(this)}>增加服務</button>
在之前的inputChange方法後新寫addList方法:
addList(){ //爲按鈕綁定增加服務項事件
this.setState({
list:[...this.state.list,this.state.inputValue]
})
}
...
這個是ES6的新語法,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
點擊添加服務按鈕可以實現添加文本框中的數據到服務列表了。
理論上每次增加服務項之後,文本框應該清空,只需要在list下面加上如下代碼即可:
inputValue:''
addList方法最終代碼:
addList(){ //爲按鈕綁定增加服務項事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
inputValue:''
})
}
需求D結束}
至此小節6_中的需求Z通過ABCD成功實現。
8_刪除服務項
需求:鼠標點擊服務項,自動刪除。
實現:
要刪除一個東西,就先獲取數組下標下標。然後爲列表添加點擊事件,併爲點擊事件綁定刪除方法deleteItem。
爲點擊事件綁定刪除方法deleteItem有兩種方法:普通方法、箭頭函數方法,區別就在於是否需要綁定this,如下代碼所示:
//return加了括號,下面的jsx就可以換行寫,看的更清楚
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return( //return加了括號,下面的jsx就可以換行寫,看的更清楚
//普通寫法
<li key={item+index} onClick={this.deleteItem.bind(this,index)}>
{item}
</li>
// 箭頭函數寫法不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
)
})
}
</ul>
deleteItem(index){ //刪除列表服務事件
// console.log(index)//測試點擊列表項能否取得相應下標
// this.state.list.splice(index,1)錯誤寫法
// this.setState({
// list:this.state.list
// }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
9_jsx中幾個要注意的地方:代碼註釋、css的class陷阱、html解析問題dangerouslySetInnerHTML
屬性、label標籤的for
1.代碼註釋:
vscode中代碼註釋快捷鍵:ctrl+/
詳見本人的另一篇博客:
jsx代碼註釋格式規範最全總結
————————————————
版權聲明:本文爲CSDN博主「Easy_Lee_willpower」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_37279880/article/details/105885633
2.jsx中css的class陷阱,應爲className
在src文件中新建style.css文件,:
style.css中的代碼:
.input {
border:3px solid #00FF00;
}
在Xiaojiejie.js頭部導入該文件:
import './style.css'
在input標籤上加上class屬性(這種寫法是錯誤的):
<input class='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/}
warning警告,爲了防止css的class與js中的class衝突,所以要把input中的class屬性改成className:
input標籤修改後:
<input className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/}
3.html解析問題的:dangerouslySetInnerHTML
屬性
想在文本框輸入<h2>標籤後,在按下增加服務後能直接渲染出來,而不是如下圖效果:
通過dangerouslySetInnerHTML 屬性實現:
在li中加入dangerouslySetInnerHTML 屬性,代碼如下:
<li
key={item+index}
nClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
4.label標籤的htmlFor
需求:點擊增加服務就能激活文本框
實現代碼如下:
<label for='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
控制檯警告:
此時把label的for改成htmlFor即可,代碼如下:
<label htmlFor='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/}
原因:怕DOM操作的htmlFor與js的for循環混淆。
10_react快速生成代碼插件:Simple React Snippets
安裝步驟:
1.點擊擴展插件
2.搜索Simple React Snippets
3.安裝install
安裝後就可通過對應的Snippets(片段)快速生成(渲染render)大段代碼:
11_組件拆分
需求:實際開發中,會把不同功能的組件拆分開寫,本項目把服務列表組件拆分。
實現:
在src目錄下新建Xiaojiejie組件的子組件XiaojiejieItem.js代碼如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li>子組件</li>
// <li>{this.props.content}</li>
);
}
}
export default XiaojiejieItem;
在Xiaojiejie.js頭部引入子XiaojiejieItem組件:
import XiaojiejieItem from './XiaojiejieItem'
並修改ul內代碼,Xiaojiejie.js代碼如下:
import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'
class Xiaojiejie extends Component{
constructor(props){
super(props)//調用父類方法(固定寫法)
this.state = {
inputValue:'',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
}
}
render(){
return(
<Fragment> {/*注意Fragment標籤首字母大寫*/}
<div>
<label htmlFor='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向Xiaojiejie*/}
<button onClick={this.addList.bind(this)}>增加服務</button>
</div>
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括號,下面的jsx就可以換行寫,看的更清楚
// 普通寫法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子組件組件寫法
<XiaojiejieItem />
)
})
}
</ul>
</Fragment>
)
}
inputChange(e){ //綁定響應事件改變文本框inputValue的值
// console.log(this);
// console.log(e);//e是一堆內容
// console.log(e.target.value);//15_通過e.target.value打印文本框內的值
// this.state.inputValue=e.target.value;//錯誤寫法:因爲沒用沒用this.setState方法
// console.log(this);//輸出undefined,所以要bind方法綁定this
// console.log(this === window);//false
this.setState({ //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
inputValue:e.target.value
})
}
addList(){ //爲按鈕綁定增加服務項事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
inputValue:''
})
}
deleteItem(index){ //刪除列表服務事件
// console.log(index)//測試點擊列表項能否取得相應下標
// this.state.list.splice(index,1)錯誤寫法
// this.setState({
// list:this.state.list
// }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
}
export default Xiaojiejie;
此時由於Array.map循環內是子組件XiaojiejieItem,而XiaojiejieItem中的li裏面的代碼又是寫死的,所以頁面如下:
在12_父子組件相互傳值的父組件向子組件傳值中,解決li數據寫死問題。
12_父子組件相互傳值
1.父組件向子組件傳值:在子組件加上屬性的形式。
步驟一:在Xiaojiejie.js的子組件XiaojiejieItem中加入了content={item}屬性:
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括號,下面的jsx就可以換行寫,看的更清楚
// 普通寫法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子組件組件寫法
<XiaojiejieItem content={item} />
)
})
}
</ul>
步驟二:在XiaojiejieItem.js子組件中,通過this.props.屬性名的方式接收父組件傳遞過來的值。
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li>{this.props.content}</li>
);
}
}
export default XiaojiejieItem;
在父組件Xiaojiejie引用子組件XiaojiejieItem時爲XiaojiejieItem加入了content={item}屬性,又在XiaojiejieItem.js子組件XiaojiejieItem的li中通過this.props.content代碼接收,解決了11_組件拆分中數據寫死的問題。
運行結果(li中數據不再寫死):
2.子組件操作父組件數據(實際上是父組件向子組件傳方法):在子組件加上屬性的形式。
功能需求:
點擊服務項實現刪除。該功能原本已經實現了,不過現在組件拆分了,就涉及了子組件向父組件傳遞數據,實際上還是靠父組件傳遞方法給子組件,子組件再用父組件的方法來操作父組件的數據。
步驟一:在Xiaojiejie.js的子組件XiaojiejieItem中加入了index、key屬性,代碼如下:
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括號,下面的jsx就可以換行寫,看的更清楚
// 普通寫法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子組件組件寫法
<XiaojiejieItem
content={item}
index={index}
key={item+index}
/>
)
})
}
</ul>
步驟二:在XiaojiejieItem.js子組件中爲li添加點擊事件handleClick,handleClick中測試是否取得數組index代碼如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
);
}
handleClick(){
console.log(this.props.index);
}
}
export default XiaojiejieItem;
實現效果如下:
點擊服務項後報錯:
即handleClick方法中的this未重新指向XiaojiejieItem ,所以要用bind重新綁定this指向:
<li onClick={this.handleClick.bind(this)}>{this.props.content}</li>
報錯消失,並實現點擊子組件服務項取得數組下標。
構造函數裏綁定this:
在構造函數裏綁定this指向。這樣綁定性能會高一些,特別是在高級組件開發中,會有很大的作用。XiaojiejieItem 的構造函數代碼如下:
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
步驟三:通過操作子組件刪除父組件裏的數據。
實現:React有明確規定,子組件時不能操作父組件裏的數據的,所以子組件需要借用一個父組件的方法來修改父組件的內容。
即子組件XiaojiejieItem借用父組件Xiaojiejie的deleteItem方法,現在要作的就是子組件調用這個方法。
父組件向子組件傳方法:
如果子組件要調用父組件方法,其實和傳遞數據差不多,父組件向子組件傳方法,也是在引用子組件時上加屬性:
在Xiaojiejie.js的子組件XiaojiejieItem中加入了deleteItem屬性:
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
/>
記得這裏也要進行this
的綁定,如果不綁定子組件是沒辦法找到這個父組件的方法的。
XiaojiejieItem 的handleClick方法代碼如下:
handleClick(){
// console.log(this.props.index)
this.props.deleteItem(this.props.index)
}
實現效果:
到此爲止,就算是實現了子組件向父組件傳值。
Xiaojiejie.js完整代碼如下:
import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'
class Xiaojiejie extends Component{
constructor(props){
super(props)//調用父類方法(固定寫法)
this.state = {
inputValue:'',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
}
}
render(){
return(
<Fragment> {/*注意Fragment標籤首字母大寫*/}
<div>
<label htmlFor='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向Xiaojiejie*/}
<button onClick={this.addList.bind(this)}>增加服務</button>
</div>
<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
<li>服務2</li> */}
{/* Easy寫法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技術胖寫法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括號,下面的jsx就可以換行寫,看的更清楚
// 普通寫法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子組件組件寫法
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
/>
)
})
}
</ul>
</Fragment>
)
}
inputChange(e){ //綁定響應事件改變文本框inputValue的值
// console.log(this);
// console.log(e);//e是一堆內容
// console.log(e.target.value);//15_通過e.target.value打印文本框內的值
// this.state.inputValue=e.target.value;//錯誤寫法:因爲沒用沒用this.setState方法
// console.log(this);//輸出undefined,所以要bind方法綁定this
// console.log(this === window);//false
this.setState({ //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
inputValue:e.target.value
})
}
addList(){ //爲按鈕綁定增加服務項事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
inputValue:''
})
}
deleteItem(index){ //刪除列表服務事件
// console.log(index)//測試點擊列表項能否取得相應下標
// this.state.list.splice(index,1)錯誤寫法
// this.setState({
// list:this.state.list
// }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
}
export default Xiaojiejie;
XiaojiejieItem.js完整代碼如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
)
}
handleClick(){
// console.log(this.props.index)
this.props.deleteItem(this.props.index)
}
}
export default XiaojiejieItem;
總結:
①父組件向子組件傳值 、子組件向父組件傳值(父組件向子組件傳方法)都是在子組件加上屬性的形式實現的,然後通過this.props.屬性名的方式訪問。
this.props:
React中,使用外部(被傳入傳入的)數據(通過 this.props 訪問),被傳入的數據可在組件中通過 this.props 在 render() 訪問。
註釋:(props:屬性properties)
this.state:
組件還可以維護其內部的有狀態的數據(通過 this.state 訪問)。
註釋:(state :狀態;有狀態的數據)
②調用方法時要綁定this指向,可以直接在調用處bind綁定,但是更好的是在組件構造函數內綁定,這樣有助於性能優化。
13_React的單項數據流、函數式編程
1.單項數據流
爲Xiaojiejie.js的子組件XiaojiejieItem加入list屬性:
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
// 單向數據流
list={this.state.list}
/>
XiaojiejieItem.js的handleClick方法加入this.props.list=[]:
handleClick(){
// console.log(this.props.index)
this.props.list=[]
this.props.deleteItem(this.props.index)
}
點擊列表項後報錯:
意思是父組件傳遞給子組件的值是隻讀的,不能在子組件中直接修改父組件的值。如果想在子組件修改父組件的值,只能通過12_父子組件相互傳值中父組件引用子組件時,給子組件傳遞父組件的方法(父組件引用子組件時爲子組件增加屬性),再用父組件的方法才能修改父組件中的值。
2.函數式編程
在面試React時,經常會問道的一個問題是:函數式編程的好處是什麼?
①函數式編程讓我們的代碼更清晰,每個功能都是一個函數。
②函數式編程爲我們的代碼測試代理了極大的方便,更容易實現前端自動化測試。
React框架也是函數式編程,所以說優勢在大型多人開發的項目中會更加明顯,讓配合和交流都得心應手。
14_react developer tools的安裝與使用
1.安裝詳見我另一篇博客:
React開發環境(Chrome瀏覽器)安裝react developer tools的兩個方法,Download the React DevTools for a better experience
https://blog.csdn.net/qq_37279880/article/details/105975542
2.react developer tools插件的三種狀態
插件有三種狀態
灰色:說明頁面不是React寫的
黑色:說明頁面是React寫的,並且處於生成環境當中。
紅色:說明頁面是用React編寫的,並且處於調試環境當中。
3.使用
打開開發者工具,發現Components,這裏你可以清晰的看到React的結構,讓自己寫的代碼更加清晰,你還可以看到組件之間的數據傳遞,再也不用通過console.log
來測試程序了,在工作中前端的調試都是在這裏進行的。
由於本學習筆記過長,圖片較多,編輯器打字時有些卡頓,所以之後內容寫在另一篇博客: