1. 列表渲染
1.1 列表渲染說明:
- 列表渲染 先將列表數(shù)據(jù)轉(zhuǎn)為React元素列表, 然后在渲染
- React會將React元素列表自動展開渲染
1.2 React元素列表渲染
說明:
- React元素列表是一個數(shù)組
- 只不過數(shù)組的每一項都是JSX語法的React元素
示例代碼:
// 不使用到組件狀態(tài),因此定義函數(shù)組件測試
function MyCom(){
// 1. react元素列表,數(shù)組的每一項都是JSX語法的React元素
// 將react元素列表保存在變量中
let oList = [
<li>蘋果</li>,
<li>香蕉</li>,
<li>梨子</li>
]
// 2.在react元素中使用{}語法將列表渲染
// react會自動遍歷列表,并將列表中的react元素進行渲染
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3 渲染列表數(shù)據(jù)
說明:
- React會將react元素列表遍歷渲染
- 因此如果要渲染列表數(shù)據(jù),就可以先將列表數(shù)據(jù)轉(zhuǎn)為react元素列表
- 遍歷列表的方式有很多中,普通for循環(huán),
for-in,for-of,forEach,map,reduce都可以使用
1.3.1 使用普通for循環(huán)遍歷列表數(shù)據(jù)
示例代碼:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. 普通for循環(huán)
let oList = [];
for(let i = 0; i< listdata.length ;i++){
// 循環(huán)列表數(shù)據(jù),將每一個數(shù)據(jù)包裹一層react元素,
// 在push到新的oList數(shù)組中,oList就是react元素列表
oList.push(<li>{listdata[i]}</li>)
}
return <ul> { oList } </ul>
}
1.3.2 使用for-in遍歷列表數(shù)據(jù)
示例代碼:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. for-in循環(huán)
let oList = [];
for(let index in listdata){
// index 為listdata列表的索引
// 通過索引取出數(shù)據(jù)包裹react元素,組成react元素列表
oList.push(<li>{listdata[index]}</li>)
}
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.3 使用for-of遍歷列表數(shù)據(jù)
示例代碼:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. for-of循環(huán)
let oList = [];
for(let value of listdata){
// for-of 直接取出數(shù)據(jù),處理為react元素.列表
oList.push(<li>{value}</li>)
}
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.4 使用forEach遍歷列表數(shù)據(jù)
示例代碼如下:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. forEach循環(huán)
let oList = [];
// forEach循環(huán)將列表數(shù)據(jù)轉(zhuǎn)為react元素列表
listdata.forEach(value => {
oList.push(<li>{value}</li>)
})
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.5 使用map遍歷列表數(shù)據(jù)
map會自動返回列表,因此不用使用push方法
示例代碼如下:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. map循環(huán)
// map 返回的就是回調(diào)函數(shù)return內(nèi)容組成的數(shù)組
let oList = listdata.map(value => {
return <li>{ value }</li>
})
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
1.3.6 使用reduce遍歷列表數(shù)據(jù)
示例代碼:
function MyCom(){
// 1. 列表數(shù)據(jù)
let listdata = ["蘋果","香蕉","梨子"]
// 2. reduce循環(huán)
let oList = listdata.reduce((prev,value) => {
// 沒事將數(shù)據(jù)包裹react元素后在push到數(shù)組中
prev.push(<li>{ value }</li>)
// 返回數(shù)組
return prev
},[])
return <ul> { oList } </ul>
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
比較常用的方法是: map和reduce兩種方法來渲染列表數(shù)據(jù)
2. 列表渲染的key屬性
2.1 可以屬性的認識
在列表渲染中使用key來標記每一項,讓React能識別每一個元素,
這樣如果遇到添加或刪除,會很有幫助,key屬性要這個元素成為唯一
key屬性的值最好是這個元素列表中獨一無二的字符串,通常我們用id來 作為元素的key當元素沒有id時,萬不得已的情況下 可以使用index索引來作為key,通常不建議,因為這會導致性能的下降
如果你不指定可以,React將默認用索引index作為key,并在控制臺提醒添加key屬性
注意:
- key只在兄弟節(jié)點之間必須唯一
- key屬性值會傳遞給React,不會傳遞給組件,如果需要 傳遞給組件,請使用其他屬性傳遞
2.2 使用key屬性渲染列表數(shù)據(jù)
// 1. 列表數(shù)據(jù)
let names = ["HTML,CSS","JavaScript","Node","Vue","React"]
// 2. 創(chuàng)建虛擬DOM
const ul = (
<ul>
{ names.map( (item,index) => <li key={index}>{item}</li>) }
</ul>
)
// 3. 渲染虛擬DOM
ReactDOM.render(ul, document.getElementById('list'))
3. 受控組件與非受控組件
3.1 受控組件與非受控制組件說明
- 受控組件是指可以被rect狀態(tài)控制的組件
- 非受控組件是可以任意輸入內(nèi)容,不受組件狀態(tài)的控制,默認是就是非受控組件
3.2 非受控組件
說明:
- 非受控組件不受react狀態(tài)影響
- 如果需要獲取非受控組件的數(shù)據(jù),使用ref屬性
示例代碼:
class MyCom extends React.Component{
// 組件狀態(tài)
state = {
username:"",
password:""
}
handleClick = () =>{
// console.log(this);
// 獲取數(shù)據(jù)
let username = this.userInput.value.trim();
let password = this.passwordInput.value.trim();
// 判斷輸入是否為空
if(!username || !password){
console.log("用戶名或者密碼不能為空")
return;
}
// console.log(username,":",password)
// 將數(shù)據(jù)添加到狀態(tài)中
this.setState(() => ({
username,password
}), () => {
console.log("數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
})
}
render(){
return (
<div>
<h2>非受控組件獲取數(shù)據(jù)</h2>
<p>
用戶名:
<input
type="text"
ref={input => this.userInput = input}
/>
</p>
<p>
密 碼:
<input type="password"
ref={input => this.passwordInput = input}
/>
</p>
<button onClick={ this.handleClick }>點擊</button>
</div>
)
}
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
3.3 受控組件
說明:
- 受控組件是指將表單綁定到狀態(tài)上,
- 狀態(tài)的值一但發(fā)生變化,輸入空的內(nèi)容也會發(fā)生變化,
- 狀態(tài)的值不變,輸入框不能輸入任何內(nèi)容
- 也就是說輸入框中的內(nèi)容被狀態(tài)所控制
示例代碼如下:
class MyCom extends React.Component{
// 組件狀態(tài)
state = {
username:"",
password:""
}
userChangeHandle = (event) =>{
// // 獲取用戶名數(shù)據(jù)
let username = event.target.value.trim();
// 判斷輸入是否為空
if(!username){
console.log("用戶名不能為空")
return;
}
// console.log(username,":",password)
// 將數(shù)據(jù)添加到狀態(tài)中
this.setState(() => ({
username
}), () => {
console.log("用戶名數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
})
}
pwChangeHandle = (event) =>{
// console.log(this);
// // 獲取密碼數(shù)據(jù)
let password = event.target.value.trim();
// 判斷輸入是否為空
if(!password){
console.log("密碼不能為空")
return;
}
// console.log(username,":",password)
// 將數(shù)據(jù)添加到狀態(tài)中
this.setState(() => ({
password
}), () => {
console.log("密碼數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
})
}
render(){
// 1. 獲取狀態(tài)中的數(shù)據(jù)
let {username, password} = this.state
// 2. 將狀態(tài)中的數(shù)據(jù)綁定到輸入框,此時輸入框就是受控組件
return (
<div>
<h2>非受控組件獲取數(shù)據(jù)</h2>
<p>
用戶名:
<input
type="text"
value={username}
onChange = { this.userChangeHandle }
/>
</p>
<p>
密 碼:
<input type="password"
value={password}
onChange = { this.pwChangeHandle }
/>
</p>
<button onClick={ this.handleClick }>點擊</button>
</div>
)
}
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
- 示例中標簽輸入內(nèi)容沒有任何限制,不被狀態(tài)控制,
- 在通過ref
示例說明:
- 輸入框的value值綁定到了狀態(tài)中的數(shù)據(jù),因此輸入框就被狀態(tài)控制
- 此時輸入框不能輸入任何內(nèi)容, 因為是狀態(tài)單向影響輸入框,而且此時還會報錯
- 如果我們希望能在輸入框輸入的內(nèi)容,就需要綁定onChange事件
- 當輸入內(nèi)容時,通過onChange綁定的事件函數(shù)獲取輸入的內(nèi)容,修改狀態(tài)值
- 狀態(tài)值一但發(fā)生變化,表單就會顯示最新的數(shù)據(jù). 看起來好像可以輸入了
4. 收集表單數(shù)據(jù)
4.1 收集標簽數(shù)據(jù)說明:
受控制和非受控組件都可以收集表單數(shù)據(jù)
但是非受控組件是在操作DOM,
受控組件不是在操作DOM, 按照react思想減少DOM操控,所以官網(wǎng)推薦受控組件
4.2 收集標簽數(shù)據(jù)代碼
使用受控組件收集標簽數(shù)據(jù)
需求
- 收集用戶輸入的用戶名,密碼
- 收集性別愛好,單選復選數(shù)據(jù)
- 收集select數(shù)據(jù)
示例代碼如下
class MyCom extends React.Component{
// 組件狀態(tài)
state = {
username:"",
password:"",
sex:"0", // 0為男,1為女
likes:[],
province:""
}
// 處理用戶名
userChangeHandle = (event) =>{
// // 獲取用戶名數(shù)據(jù)
let username = event.target.value.trim();
// 判斷輸入是否為空
if(!username){
console.log("用戶名不能為空")
return;
}
// console.log(username,":",password)
// 將數(shù)據(jù)添加到狀態(tài)中
this.setState(() => ({
username
}))
}
// 處理密碼
pwChangeHandle = (event) =>{
// console.log(this);
// // 獲取密碼數(shù)據(jù)
let password = event.target.value.trim();
// 判斷輸入是否為空
if(!password){
console.log("密碼不能為空")
return;
}
// console.log(username,":",password)
// 將數(shù)據(jù)添加到狀態(tài)中
this.setState(() => ({
password
}))
}
// 處理性別 單選
changeSex = (event) => {
let sex = event.target.value;
this.setState(() => ({
sex
}))
}
// 處理愛好 多選
changeLike = (event) => {
// 獲取復選框改變的數(shù)據(jù)
let like = event.target.value;
// 獲取狀態(tài)中收集愛好的數(shù)據(jù)
let {likes} = this.state
// 判斷狀態(tài)中是否存在,存在就刪除,不存在就添加
if(!likes.includes(like)){
likes.push(like)
}else{
likes = likes.filter(value => {
return like != value
})
}
// 更新數(shù)據(jù)
this.setState(() => ({
likes
}))
}
// 獲取省份數(shù)據(jù)
changeProvince = (event) => {
let province = event.target.value;
this.setState(() => ({
province
}))
}
render(){
// 1. 獲取狀態(tài)中的數(shù)據(jù)
let {
username,
password,
sex,
likes,
province
} = this.state
// 2. 將狀態(tài)中的數(shù)據(jù)綁定到輸入框,此時輸入框就是受控組件
return (
<div>
<h2>非受控組件獲取數(shù)據(jù)</h2>
<p>
用戶名:
<input
type="text"
value={username}
onChange = { this.userChangeHandle }
/>
</p>
<p>
密 碼:
<input type="password"
value={password}
onChange = { this.pwChangeHandle }
/>
</p>
<p>
性別:
<label htmlFor="male">
<input
id="male"
type="radio"
value="0"
checked={sex == 0 }
onChange = { this.changeSex }
/>
男
</label>
<label htmlFor="female">
<input
id="female"
type="radio"
value="1"
checked={sex == 1 }
onChange = { this.changeSex }
/>
女
</label>
</p>
<p>
愛好:
<label htmlFor="like1">
<input
id="like1"
type="checkbox"
value="游泳"
checked={ likes.some(like => like == "游泳") }
onChange = { this.changeLike }
/>
游泳
</label>
<label htmlFor="like2">
<input
id="like2"
type="checkbox"
value="籃球"
checked={ likes.some(like => like == "籃球") }
onChange = { this.changeLike }
/>
籃球
</label>
<label htmlFor="like3">
<input
id="like3"
type="checkbox"
value="足球"
checked={ likes.some(like => like == "足球") }
onChange = { this.changeLike }
/>
足球
</label>
</p>
<p>
省份:
<select
value={province}
onChange={this.changeProvince }
>
<option value="">請選擇</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="廣東">廣東</option>
<option value="安徽">安徽</option>
<option value="湖南">湖南</option>
</select>
</p>
<button onClick={ this.handleClick }>點擊</button>
</div>
)
}
}
// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
示例說明:
- 用戶名,密碼表單受控組件直接綁定value值
- 單選復選通過checked判斷是否選擇中,因此將狀態(tài)中用來數(shù)據(jù)對應數(shù)據(jù)字段的數(shù)據(jù)跟value對比
- select受控組件收集數(shù)據(jù),將value綁定在select標簽上
5. 虛擬DOM與DOM Diff算法

01.png
初始化顯示界面
- 創(chuàng)建虛擬DOM樹(一般JS對象)
- 真實DOM樹
- 將虛擬DOM添加真實DOM繪制界面顯示
初始化看不出虛擬DOM的優(yōu)勢,優(yōu)勢主要體現(xiàn)在更新,因為頁面大部分情況下都是在更新
更新界面
- setState() 更新狀態(tài)
- 重新創(chuàng)建虛擬DOM樹
- 新/舊樹比較差異
- 更新差異對應的真實DOM
- 局部界面重繪
為什么狀態(tài)不能直接就改,而是要通過setState,因為直接修改沒法比較,setState會重新創(chuàng)建新狀態(tài),生成新的虛擬DOM,可以與原來狀態(tài)和虛擬DOM進行對比
例子演示;
// 1. 定義組件
class Hello extends React.Component {
constructor (){
super()
// 定義狀態(tài)
this.state = {
date: new Date()
}
}
componentWillMount(){
setInterval(()=>{
this.setState({
date: new Date()
})
},1000)
}
render(){
console.log('render()')
let {date} = this.state;
return (
<div>
輸入內(nèi)容: <input type="text" /> <span>{date.toTimeString()}</span>
</div>
)
}
}
// 2. 渲染組件標簽
ReactDOM.render(
<Hello />,
document.getElementById('box')
)