react-router之Route渲染內(nèi)容的三種方式
結(jié)論: Route渲染優(yōu)先級(jí): children > component > render
component
- 只有path匹配時(shí),組件才呈現(xiàn)。匹配user路徑則渲染UserPage
<Route path="/user" component={UserPage} />
官方: When you use
component(instead ofrenderorchildren, below) the router usesReact.createElementto create a new React element from the given component. That means if you provide an inline function to thecomponentprop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use therenderor thechildrenprop (below).翻譯: 當(dāng)你使用component(而不是render, children), router使用React.createElement從給定組件創(chuàng)建一個(gè)新的React元素。
這意味著如果您為組件道具提供內(nèi)聯(lián)函數(shù),那么您將在每次呈現(xiàn)時(shí)創(chuàng)建一個(gè)新組件。這將導(dǎo)致現(xiàn)有組件卸載和新組件掛載,而不只是更新現(xiàn)有組件。
當(dāng)使用內(nèi)聯(lián)函數(shù)進(jìn)行內(nèi)聯(lián)呈現(xiàn)時(shí),請(qǐng)使用render或children道具(下面)。
Render: func()
- render可以方便地進(jìn)行內(nèi)聯(lián)呈現(xiàn)和包裝,而不需要進(jìn)行不必要的重新加載
官方: Instead of having a new React element created for you using the
componentprop, you can pass in a function to be called when the location matches. Therenderprop function has access to all the same route props (match, location and history) as thecomponentrender prop翻譯: 不使用components屬性為您創(chuàng)建一個(gè)新的React元素,使用render您可以傳入一個(gè)函數(shù),以便在位置匹配時(shí)調(diào)用。渲染道具功能可以訪問(wèn)所有相同的路徑道具(match,location和history)作為組件渲染道具
案例
- 給路由組件傳遞一個(gè)count屬性
// app.js
import React, { useState } from 'react'
import { BrowserRouter as Router, Link, Route } from 'react-router-dom'
import UserPage from './views/UserPage'
import HomePage from './views/HomePage'
import LoginPage from './views/LoginPage'
function App() {
const [count, setCount] = useState(0)
return (
<div className="App">
<div>
Count : {count}
<button onClick={() => setCount(count + 1)}>我是按鈕</button>
</div>
<Router>
<Link to="/">首頁(yè)</Link>
<Link to="/user">用戶</Link>
<Link to="/home">主頁(yè)</Link>
<Link to="/login">登錄</Link>
{/* component 屬性 */}
<Route path="/user" component={() => <UserPage count={count} />} />
{/* <Route path="/user" render={() => <UserPage count={count} />} /> */}
<Route
path="/home"
render={() => {
console.log('homepage')
return <HomePage />
}}
/>
<Route path="/login" children={LoginPage} />
</Router>
</div>
)
}
export default App
- 此時(shí)采用了component屬性, 傳遞count給UserPage
<Route path="/user" component={() => <UserPage count={count} />} />
- UserPage.js中展示
// userPage.js
import React, { PureComponent } from 'react'
class UserPage extends PureComponent {
componentDidMount() {
console.log('UserPage渲染了。。')
}
componentWillUnmount() {
console.log('userPage卸載了。。。')
}
render() {
// 掛載了 history: location match:三個(gè)對(duì)象
console.log('UserPage prosp', this.props)
return <div>UserPage == Count:{this.props.count}</div>
}
}
export default UserPage
- 點(diǎn)擊按鈕時(shí), 發(fā)生如下UserPage 重復(fù)渲染,
- react在比較組件狀態(tài)以便決定如何更新dom節(jié)點(diǎn)時(shí),首先要比較組件的type和key。
- 所以component使用內(nèi)聯(lián)函數(shù)時(shí), 會(huì)調(diào)用React.createElement創(chuàng)建元素,傳入時(shí)一個(gè)匿名函數(shù),組件每次都會(huì)生成一個(gè)新的匿名函數(shù),導(dǎo)致生成組件的type不相同, 造成組件的掛載,卸載現(xiàn)象

- 采用官方推薦方案,采用render寫(xiě)法, 內(nèi)聯(lián)函數(shù)
<Route path="/user" render={() => <UserPage count={count} />} />

children: func
- 不管是否存在匹配項(xiàng),childern都可以渲染一些東西,其他使用和render一樣
- 修改為 children屬性
<Route path="/user" children={() => <UserPage count={count} />} />

Route核心渲染源碼
return (
<RouterContext.Provider value={props}>
{match // 是否與地址匹配
? children // 1.優(yōu)先判斷children是否存在
? typeof children === 'function' // 存在情況下判斷children是否是個(gè)函數(shù)
? children(props)
: children
: component // 2.判斷是否是一個(gè)component,
? React.createElement(component.props) // 是, 創(chuàng)建一個(gè)元素
: render // 3. 判斷是否是render屬性; render傳入是一個(gè)函數(shù)
? render(props) // 執(zhí)行render
: null // 否, 輸出是空的
: children // 不匹配,判斷是否是有children
? typeof children === 'function' // 判斷children是否是個(gè)函數(shù)
? children(props) // 執(zhí)行函數(shù)
: children // 組件直接渲染
: null}
</RouterContext.Provider>
)