邢臺做wap網(wǎng)站費(fèi)用網(wǎng)站接廣告平臺
文章目錄
- PureComponent
- 1. 使用 React.Component,不會進(jìn)行淺比較
- 2. 使用 shouldComponentUpdate 生命周期鉤子,手動比較
- 3. 使用 React.PureComponent,自動進(jìn)行淺比較
- Render Props
- 1. 使用 Children props(通過組件標(biāo)簽體傳入結(jié)構(gòu))
- 2. 使用 Render Props(通過組件標(biāo)簽屬性傳入結(jié)構(gòu))
- Error Boundaries(錯誤邊界)
PureComponent
PureComponent 在其內(nèi)部實(shí)現(xiàn)了 shouldComponentUpdate()
方法的淺比較邏輯。當(dāng)組件的 props 或 state 發(fā)生變化時(shí),React 會通過這個方法檢查前后的 props 和 state 是否相等。
如果 shouldComponentUpdate()
返回 false,則 React 不會觸發(fā)組件的重新渲染過程,這可以避免不必要的渲染和可能帶來的性能開銷。
1. 使用 React.Component,不會進(jìn)行淺比較
當(dāng)執(zhí)行this.setState({})
時(shí):
<Parent />組件
中 state 沒有發(fā)生變化,也會重新執(zhí)行render()
<Child />組件
沒有接收任何props,也會重新執(zhí)行render()
.
不管父子組件的 props 或 state 有沒有發(fā)生變化,都會重新執(zhí)行各自的render(),重新渲染組件。這顯然不符合邏輯,效率低下
import React, { Component } from 'react'export default class Parent extends Component {state = { name: '張三' }changeName = () => {this.setState({})}render () {console.log('Parent render');return (<div><button onClick={this.changeName}>點(diǎn)擊</button><Child /></div>)}
}class Child extends Component {render () {console.log('Child render');return (<div>Child</div>)}
}
.
2. 使用 shouldComponentUpdate 生命周期鉤子,手動比較
使用 shouldComponentUpdate 生命周期鉤子,手動判斷 props 或 state 的發(fā)生變化:
- 如果父組件數(shù)據(jù)沒有更新,則父組件和子組件都不需要重新render():
父組件 => shouldComponentUpdate () { return false }
- 如果父組件數(shù)據(jù)有更新,則父組件需要重新render():
父組件 => shouldComponentUpdate () { return true }
- 子組件則判斷props數(shù)據(jù)有沒有更新:
子組件 => shouldComponentUpdate (nextProps, nextState) { return !this.props.name === nextProps.name }
import React, { Component, PureComponent } from 'react'export default class Parent extends Component {state = { name: '張三' }changeName = () => {this.setState({})}shouldComponentUpdate (nextProps, nextState) {// 當(dāng)執(zhí)行 this.setState({})時(shí),state中數(shù)據(jù)沒有變化時(shí), return false 阻止更新.這樣就不會重新渲染Parent組件,Child組件也不會更新// 當(dāng)執(zhí)行 this.setState({ name: '李四' })時(shí),state中數(shù)據(jù)發(fā)生改變, return true 這會重新渲染Parent組件,這是Child組件的props會改變if (this.state.name === nextState.name) {return false} else {return true}}render () {console.log('Parent render');return (<div><button onClick={this.changeName}>點(diǎn)擊</button><Child name={this.state.name} /></div>)}
}class Child extends Component {// 如果接收到的props中數(shù)據(jù)沒有變化,則return false,阻止組件更新shouldComponentUpdate (nextProps, nextState) {if (this.props.name === nextProps.name) {return false} else {return true}}render () {console.log('Child render');return (<div>Child{this.props.name}</div>)}
}
3. 使用 React.PureComponent,自動進(jìn)行淺比較
PureComponent 在其內(nèi)部實(shí)現(xiàn)了 shouldComponentUpdate()
方法的淺比較邏輯。當(dāng)組件的 props 或 state 發(fā)生變化時(shí),React 會通過這個方法檢查前后的 props 和 state 是否相等。
import React, { PureComponent } from 'react'export default class Parent extends PureComponent {state = { name: '張三' }changeName = () => {this.setState({})}// 相當(dāng)于PureComponent中內(nèi)置shouldComponentUpdate的判斷邏輯// shouldComponentUpdate (nextProps, nextState) {// return !this.state.name === nextState.name// }render () {console.log('Parent render');return (<div><button onClick={this.changeName}>點(diǎn)擊</button><Child name={this.state.name} /></div>)}
}class Child extends PureComponent {// 相當(dāng)于PureComponent中內(nèi)置shouldComponentUpdate的判斷邏輯// shouldComponentUpdate (nextProps, nextState) {// return !this.props.name === nextProps.name// }render () {console.log('Child render');return (<div>Child{this.props.name}</div>)}
}
Render Props
在React中,并沒有直接對應(yīng)于 Vue.js 中的 “插槽”(slot)的概念。Vue.js的插槽允許你在子組件的模板中預(yù)留一些占位符,這些占位符可以由父組件來填充內(nèi)容。
然而,React通過其組合和props傳遞的特性,提供了類似的功能,但實(shí)現(xiàn)方式略有不同。
1. 使用 Children props(通過組件標(biāo)簽體傳入結(jié)構(gòu))
在子組件中可以通過 this.props.children
接收并渲染父組件傳遞的任何內(nèi)容
import React, { Component } from 'react'export default class Parent extends Component {state = { name: '張三' }render () {return (<Child><p>這是一段文本。</p><button>這是一個按鈕。</button></Child>)}
}class Child extends Component {render () {return (<div className='childNode'>{/* 獲取到<Child>中的標(biāo)簽屬性 */}{this.props.children} // 這里可以接收并渲染父組件傳遞的任何內(nèi)容</div>)}
}
把 <Child>
內(nèi)部的兩個節(jié)點(diǎn)插入到 .childNode
中
多層嵌套
-
通過
<Child> <Grand/> </Child>
的形式,形成父子組件 -
通過
{this.props.children}
的形式,渲染內(nèi)容
import React, { Component } from 'react'// 父組件
export default class Parent extends Component {state = { name: '張三' }render () {return (<Child>{/* Grand是Child的子組件 */}<Grand name={this.state.name} /></Child>)}
}// 子組件
class Child extends Component {state = { name: '李四' }render () {return (<div className='childNode'><p>Child組件</p>{this.props.children}</div>)}
}// 孫子組件
class Grand extends Component {render () {return (<div className='grandNode'><p>Grand組件</p><p>{this.props.name}</p></div>)}
}
但這樣 Child 組件
里的數(shù)據(jù)(state = { name: '李四' }
)無法傳遞給 Grand 組件
,因此需要使用 Render Props
.
2. 使用 Render Props(通過組件標(biāo)簽屬性傳入結(jié)構(gòu))
Render props是一種在React組件之間共享代碼的模式。一個組件接收一個返回React元素的函數(shù)作為prop,并在其渲染方法中調(diào)用該函數(shù)。這使得父組件能夠動態(tài)地決定子組件應(yīng)該渲染什么內(nèi)容。
-
通過
<Child render={() => <Grand />} />
的形式,形成父子組件:render prop
的模式來在Child 組件
中動態(tài)渲染Grand 組件
-
通過
{this.props.render()}
的形式,動態(tài)渲染內(nèi)容
import React, { Component } from 'react'// 父組件
export default class Parent extends Component {state = { name: '張三' }render () {return (// 通過 render prop 的模式來在 Child 組件中動態(tài)渲染 Grand 組件<Child render={(name) => <Grand name={name} />} />)}
}// 子組件
class Child extends Component {state = { name: '李四' }render () {return (<div className='childNode'><p>Child組件</p>{/* <Grand>組件被放置在這里,并傳入Child組件的數(shù)據(jù)(name: '李四') */}{/* 相當(dāng)與vue中<slot>插槽,動態(tài)渲染的位置 */}{this.props.render(this.state.name)} </div>)}
}// 孫子組件
class Grand extends Component {render () {return (<div className='grandNode'><p>Grand組件</p><p>{this.props.name}</p></div>)}
}
這樣 Child 組件
里的數(shù)據(jù)(state = { name: '李四' }
)就可以傳遞給 Grand 組件
了
Error Boundaries(錯誤邊界)
正常情況下,子組件在任何位置的報(bào)錯,都會使整個頁面崩潰。
import React, { Component } from 'react'// 父組件
export default class Parent extends Component {render () {return (<div><h2>父組件Parent</h2><Child /></div>)}
}// 子組件
class Child extends Component {state = {list: '', // 會導(dǎo)致 this.state.list.map 報(bào)錯,從而使整個頁面崩潰// list: [// { id: 1, name: '張三' },// { id: 2, name: '李四' },// { id: 3, name: '王五' },// ]}render () {return (<ul>{this.state.list.map((item) => {return <li key={item.id}>{item.name}</li>})}</ul>)}
}
上例中,state.list=''
會導(dǎo)致 this.state.list.map
報(bào)錯,從而使整個頁面被摧毀。
.
使用 Error Boundaries 可以把錯誤被限定在指定的邊界,并捕獲錯誤信息,來確保單個錯誤或異常不會導(dǎo)致整個應(yīng)用程序或頁面崩潰。
在 React 中,Error Boundaries 是一種特殊的組件,可以捕獲并打印發(fā)生在其子組件樹任何位置的 JavaScript 錯誤,并且,它會阻止整個 React 組件樹被這個錯誤摧毀。當(dāng)錯誤被捕獲后,Error Boundary 可以選擇顯示一個降級 UI 而不是崩潰的組件樹,這允許用戶可以繼續(xù)使用應(yīng)用的其他部分。
要使用 Error Boundaries,需要創(chuàng)建一個類組件,并在其中實(shí)現(xiàn) static getDerivedStateFromError()
和 componentDidCatch()
這兩個生命周期方法。
- static getDerivedStateFromError() : 當(dāng)子組件出現(xiàn)報(bào)錯時(shí),會觸發(fā)該回調(diào),并攜帶錯誤信息
- componentDidCatch(): 渲染組件時(shí)出錯會被調(diào)用,常用于統(tǒng)計(jì)錯誤信息并傳給后臺
注意:
- 只有生產(chǎn)環(huán)境才生效
- 只能捕獲后代組件生命周期中拋出的錯誤,不能捕獲自身組件產(chǎn)生的錯誤
- 不是生命周期的錯誤無法捕獲
import React, { Component } from 'react'// 父組件
export default class Parent extends Component {state = {hasError: ''// 用于標(biāo)識子組件是否產(chǎn)生錯誤}// 當(dāng)Parent的子組件出現(xiàn)報(bào)錯時(shí),會觸發(fā)getDerivedStateFromError調(diào)用,并攜帶錯誤信息static getDerivedStateFromError (error) {console.log('getDerivedStateFromError:', error);return { hasError: error } // 返回一個error狀態(tài),并更新state中的hasError}componentDidCatch (error, errorInfo) {console.log('渲染組件時(shí)出錯會被調(diào)用');// 可以將錯誤日志上報(bào)給服務(wù)器 console.error("Caught an error in ErrorBoundary", error, errorInfo);// 還可以將錯誤信息存儲在 state 中 // this.setState({ // error: error, // errorInfo: errorInfo // });}render () {return (<div><h2>父組件Parent</h2>{this.state.hasError ? <h2>子組件出錯</h2> : <Child />}</div>)}
}// 子組件
class Child extends Component {state = {list: '',// list: [// { id: 1, name: '張三' },// { id: 2, name: '李四' },// { id: 3, name: '王五' },// ]}render () {return (<ul>{this.state.list.map((item) => {return <li key={item.id}>{item.name}</li>})}</ul>)}
}
父組件正常顯示,報(bào)錯的子組件則顯示 <h2>子組件出錯</h2>