react组件和渲染之间的路由器差异

IT技术 reactjs react-router
2021-03-23 00:10:24

我真的不明白在react-router的路由中渲染和组件props之间的区别,在文档中它说渲染不会创建新元素但组件会,我试图回顾历史,但我发现 componentWillMount 在我时被调用在 Route 中使用渲染,它们是什么意思“如果您为组件属性提供内联函数,您将在每次渲染时创建一个新组件。这会导致现有组件卸载和新组件安装,而不是仅仅更新现有组件.”

4个回答

源代码说明了区别:

if (component)
  return match ? React.createElement(component, props) : null

if (render)
  return match ? render(props) : null

当您使用componentprop 时,每次调用Route#render. 这意味着,对于您传递给componentRoute 的 prop、构造函数componentWillMount、 和的组件,componentDidMount将在每次渲染路由时执行。

例如,如果你有

<Route path="/:locale/store" component={Store} />

并且用户导航到 /en/store,然后转到其他地方,然后导航回 /en/store,Store 组件将被挂载,然后卸载,然后再次挂载。这类似于做

<Route path="/:locale/store">
  <Store />
</Route>

与此相比,如果使用renderprops,该组件评估每个Route#render还记得每个组件都是一个函数吗?此函数将按原样执行,没有任何生命周期方法。所以当你拥有它时

<Route path="/:locale/store" render={Store} />

你可以把它想象成

<Route path="/:locale/store">
  {Store()}
</Route>

它节省了你的运行时间,因为没有运行生命周期方法,但它也有一个缺点,如果 Store 组件有一些像 shouldComponentUpdate 这样的 post-mount 生命周期方法也可以提高性能。


在 Medium 上一篇关于这个性能黑客的好帖子,请看一看。它写得很好,也适用于 React 16。

所以我可以这么说,我使用带有函数组件的渲染和带有类组件的组件
2021-05-23 00:10:24
非常感谢,我只是很困惑,因为当我使用渲染时,我发现 componentWillMount 被调用,如果我在某个时候更改状态并转到另一条路线,然后返回到我的组件,我发现初始状态不是更改的状态,文档不是说“更新现有组件而不是卸载它”
2021-06-08 00:10:24
{Store()} 如果这个组件已经有生命周期方法,如果我这样称呼它,它将如何在没有任何生命周期方法的情况下执行
2021-06-15 00:10:24
一个警告是,如果您在 中使用<Route path="/:locale/store" render={Store} />并且还使用了 react hooks Store,那么它就不会起作用,因为 react hooks 只能在函数组件中调用,而不是在常规函数中调用。
2021-06-15 00:10:24
在对那个 Medium 帖子的回应中,确认与声明为类的有状态组件相比,react@16 中的第二个选项甚至更快
2021-06-17 00:10:24

所以我也对文档的这一部分感到困惑,但我终于弄明白了。

明白这是语句的关键“提供了一个内联函数到组件props

我们都知道Route组件在位置改变时会重新渲染,react会比较新旧虚拟DOM树,得到一些diff结果并应用到真实DOM上。

并且react 会尽量重用DOM 节点,除非新的ReactElementtypekey prop 发生变化。

所以

// 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&old ReactElement类型是匿名函数的两个不同实例

() => <App />

所以在 React 的观点中,有不同类型的元素,应该使用unmount old > mount new操作来处理,这意味着每次父组件重新渲染时,你对旧组件所做的每个状态或更改都会丢失。

但是为什么 render prop 避免了卸载和挂载行为?也是匿名函数!?

这里我想参考@Rishat Muhametshin贴的代码,Route组件的render方法的核心部分:

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 而不是 component prop 时,render prop函数返回的元素类型不会在每次渲染时改变,即使在每个 parentElement.render() 上总是创建一个新的匿名函数实例

在我看来,您可以通过为匿名函数命名来实现 render prop 对组件 prop 所做的相同行为:

// Put this line outside render method.
const CreateAppComponent = () => <AppComponent />

// Inside render method
render(){
  return <Route component={CreateAppComponent}/>
}

所以结论是,如果直接使用 component={AppComponent} 的话,component 和 render prop 的性能没有区别,如果你想给 AppComponent 分配一些 props,使用 render={() => <AppComponent {...props}/> }而不是component={() => <AppComponent {...props}/> }

完美的!我想我终于明白了!谢谢!
2021-06-09 00:10:24

大多数概念已被其他答案解释过,让我通过以下方式对其进行整理:

首先,我们有源代码

if (component)
  return match ? React.createElement(component, props) : null

if (render)
  return match ? render(props) : null

案例#1:没有功能的组件

<Route path="/create" component={CreatePage} />

React.createElement(CreatePage, props)由于React.createElement(component, props)来自源代码而被调用实例化将导致重新安装

案例#2:无功能渲染

<Route path="/create" render={CreatePage} />

React.createElement(CreatePage, props) 通入渲染丙之前调用,然后通过被称为render(props)从源代码。没有实例化,没有重新安装。

案例#3:具有功能的组件

<Route path="/create" component={ () => <CreatePage /> } />

React.createElement(CreatePage, props)被调用两次首先用于 jsx 解析(匿名函数),首先用于返回CreatePage匿名函数的实例,其次来自源代码。那么为什么不在组件 prop 中这样做呢?

oligofren指出的错误

解析 JSX 不会调用它。它只是最终创建了函数表达式。你不想做 #3 的原因是你每次都创建一个新的匿名类型,导致重新挂载 dom。

案例#4:用函数渲染

<Route path="/create" render={ () => <CreatePage /> } />

每次路由到path=/create. 感觉像案例 #1吗?

结论

根据这四种情况,如果我们想把prop传给Component,就需要使用case #4来防止重新挂载。

<Route path="/abc" render={()=><TestWidget num="2" someProp={100}/>}/>

这离主题有点远,所以我留下官方讨论以供进一步阅读。

是的,你是对的,我当时写的不好,重要的是anonymous调用时创建一次,源代码,总共两次感谢您指出您的反对意见,提醒我错误。
2021-05-24 00:10:24
但是你的回答仍然说它被调用了两次,但是当你将它分配给renderprop时不会调用匿名函数它被创建一次(当你分配它时)并且直到调用它render()方法Route才被调用。并且仅通过该调用React.createElement调用,因为它隐含地是匿名函数的一部分(在 JSX 解析之后)。
2021-05-29 00:10:24
对不起,我误会你了。我有一段时间没有使用 React,也许我应该参考你的评论:)
2021-06-08 00:10:24
“React.createElement(CreatePage, props) 被调用两次。”不。不会。解析 JSX 不会调用它。它只是最终创建了函数表达式。你不想做 #3 的原因是你每次都创建一个新的匿名类型,导致重新挂载 dom。
2021-06-20 00:10:24

即使我们不向 传递任何propsComponentToRender,我也发现使用render而不是component有一些好处默认情况下在使用component<Route \>将附加 props( { history, location, match })传递我们也可以通过渲染回调访问这个props,但我们也可以省略它。为什么我们需要它?父级或任何导航的每个渲染(即使将路由更改为与以前相同)都会创建新对象。所以当我们把它传给我们的时候,我们每次都会得到新的props,什么可能会导致一些性能问题,尤其是.ComponentToRender<Route />'smatchComponentToRenderPureComponent