首页 文章

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

提问于
浏览
15

我真的没有区分反应路由器中的路由器中的渲染和组件道具,在文档中它表示渲染不会创建新元素但组件确实如此,我试图回溯历史但我发现当我调用componentWillMount时在Route中使用render,它们是什么意思“如果你为组件属性提供内联函数,你将在每次渲染时创建一个新组件 . 这会导致现有组件卸载和新组件安装,而不仅仅是更新现有组件“ .

2 回答

  • 25

    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的组件,构造函数, componentWillMountcomponentDidMount 将在每次呈现路径时执行 .

    例如,如果你有

    <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 Store组件有一些post-mount生命周期方法,如shouldComponentUpdate,也可以提高性能 .


    a good post on Medium about this performance hack,请看一下 . 它编写得非常好,也适用于React 16 .

  • 19

    所以我对这部分文档感到困惑,但我终于明白了 .

    理解这一点的关键是声明“ provide an inline function to the component prop

    我们都知道Route组件会在位置发生变化时重新渲染,并且react会比较旧的和新的虚拟DOM树,得到一些diff结果并应用于真正的DOM .

    并且react会尝试最好重用DOM节点,除非新的ReactElement的 typekey 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的方式创建一个新的匿名函数,所以 type of new&old ReactElement 是匿名函数的两个不同实例

    () => <App />
    

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

    但为什么渲染道具避免了卸载和挂载行为?这也是一个匿名函数!?

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

    在我看来,你可以通过给匿名函数命名来存档渲染道具与组件道具相同的行为:

    // Put this line outside render method.
    const CreateAppComponent = () => <AppComponent />
    
    // Inside render method
    render(){
      return <Route component={CreateAppComponent}/>
    }
    

    所以结论是,如果你直接使用component = ,那么组件和渲染道具之间的性能不同,如果你想为AppComponent分配一些道具,请使用 render={() => <AppComponent {...props}/> } 而不是 component={() => <AppComponent {...props}/> }

相关问题