我真的没有区分反应路由器中的路由器中的渲染和组件道具,在文档中它表示渲染不会创建新元素但组件确实如此,我试图回溯历史但我发现componentWillMount是当我在Route中使用render时调用它们是什么意思“如果你为组件属性提供内联函数,你将在每次渲染时创建一个新组件。这会导致现有组件卸载和新组件安装而不仅仅是更新现有的组件。“
答案 0 :(得分:43)
The source code说明不同之处:
if (component)
return match ? React.createElement(component, props) : null
if (render)
return match ? render(props) : null
当您使用component
prop时,每次调用Route#render
时都会实例化该组件。这意味着,对于传递给Route component
prop的组件,构造函数componentWillMount
和componentDidMount
将在每次呈现路径时执行。
例如,如果你有
<Route path="/:locale/store" component={Store} />
并且用户导航到/ en / store,然后转到其他地方,然后导航回/ en / store,Store组件将被挂载,然后卸载,然后再次挂载。它类似于做
<Route path="/:locale/store">
<Store />
</Route>
与此相比,如果您使用render
prop,则每个Route#render
上的组件都会评估。还记得每个组件都是一个功能吗?此函数将按原样执行,无需任何生命周期方法。所以当你拥有它时
<Route path="/:locale/store" render={Store} />
您可以将其视为
<Route path="/:locale/store">
{Store()}
</Route>
它保存了运行时,因为没有运行生命周期方法,但它也有一个缺点,例如,Store组件有一些post-mount生命周期方法,如shouldComponentUpdate,也可以提高性能。
有a good post on Medium about this performance hack,请看一下。它编写得非常好,也适用于React 16。
答案 1 :(得分:25)
所以我也对这部分文档感到困惑,但我终于明白了。
理解这一点的关键是声明“为组件道具提供内联函数”
我们都知道Route组件会在位置发生变化时重新渲染,并且react会比较旧的和新的虚拟DOM树,得到一些diff结果并应用到真正的DOM。
并且react会尝试最好重用DOM节点,除非新的ReactElement的类型或键道具已更改。
所以
// 1.
const componentA = React.createElement(App, props)
const componentB = React.createElement(App, props)
console.log(componentA.type === componentB.type) // true
// 2.
const componentA = React.createElement(() => <App />, props)
const componentB = React.createElement(() => <App />, props)
console.log(componentA.type === componentB.type) // false
通过方式1创建的所有ReactElements都具有相同的类型(App组件),但如果它们都是通过方式2创建的,则它们的类型不同。
为什么?
因为在调用父组件(包含Route组件的组件)render方法时,总是会以2的方式创建一个新的匿名函数,所以类型的new&amp; old ReactElement 是两个匿名函数的不同实例
() => <App />
所以在React的观点中,有不同的类型元素,应该用 unmount old&gt;来处理mount new 操作,这意味着每次重新渲染父组件时,您对旧组件所做的每个状态或更改都会丢失。
但为什么渲染道具避免了卸载和挂载行为?这也是一个匿名函数!?
这里我想引用@Rishat Muhametshin发布的代码,这是Route组件渲染方法的核心部分:
if (component)
// We already know the differences:
// React.createElement(component)
// React.createElement(() => <component/>)
return match ? React.createElement(component, props) : null
if (render)
return match ? render(props) : null
render prop是一个在调用时返回ReactElement的函数,返回元素的类型是什么?
<Route render={() => <AppComponent />}></Route>
它是AppComponent,而不是匿名函数包装器!因为在jsx编译之后:
render = () => React.createElement(AppComponent)
render() = React.createElement(AppComponent)
React.createElement(render) =
React.createElement(() => React.createElement(AppComponent))
React.createElement(render()) =
React.createElement(React.createElement(AppComponent))
所以当你使用render而不是组件prop时,渲染prop 函数返回的元素类型在每次渲染时都不会改变,即使总是在每个parentElement.render上创建一个新的匿名函数实例()
在我看来,你可以通过为匿名函数命名来存档渲染道具与组件道具相同的行为:
// Put this line outside render method.
const CreateAppComponent = () => <AppComponent />
// Inside render method
render(){
return <Route component={CreateAppComponent}/>
}
所以结论是,如果你直接使用component = {AppComponent},那么组件和渲染道具之间没有性能差异,如果你想为AppComponent分配一些道具,请使用
render={() => <AppComponent {...props}/> }
代替component={() => <AppComponent {...props}/> }
答案 2 :(得分:6)
大多数概念已由其他答案解释,让我按以下顺序进行梳理:
首先,我们有source code:
if (component)
return match ? React.createElement(component, props) : null
if (render)
return match ? render(props) : null
<Route path="/create" component={CreatePage} />
由于源代码中的React.createElement(CreatePage, props)
而调用 React.createElement(component, props)
。实例化将导致重新安装。
<Route path="/create" render={CreatePage} />
React.createElement(CreatePage, props)
已被调用,然后传递给渲染道具,然后由render(props)
从源代码调用。没有实例,没有重新安装。
<Route path="/create" component={ () => <CreatePage /> } />
React.createElement(CreatePage, props)
被称为两次。首先用于jsx解析(匿名函数),其次来自源代码。那么,为什么不在prop属性中执行此操作呢?
<Route path="/create" render={ () => <CreatePage /> } />
每次路由到path=/create
时都有一个实例化(jsx解析)。感觉像是案例1 ?
根据这四种情况,如果要传递道具到组件,则需要使用情况#4 来防止重新安装。
<Route path="/abc" render={()=><TestWidget num="2" someProp={100}/>}/>
这与本主题有点相距甚远,因此我将official discussion留作进一步阅读。
答案 3 :(得分:0)
即使我们没有将任何道具传递给ComponentToRender
,我也发现使用 render 而不是 component 有一些好处。
默认情况下,在使用 component 时,<Route \>
将附加道具({ history, location, match }
)传递给ComponentToRender
。我们也可以通过 render 回调访问此道具,但也可以忽略它。
我们为什么需要它? <Route />'s
父级的任何渲染或任何导航(即使将路由更改为与以前相同的路径)也会创建新的match
对象。
因此,当我们将其传递给ComponentToRender
时,每次都会获得新的道具,这可能会导致一些性能问题,尤其是PureComponent
。