Antd Design Pro2 多页签下,两个路由使用同一组件,页面如何独立

#1

https://segmentfault.com/q/1010000016866230

求助朋友们。
Antd Design Pro2 多页签下,两个路由使用同一组件,页面如何独立

链接里面就是具体问题描述。朋友们,若有解决方案,恳请留言相助。

#3

???????

#4

这种标签模式在本地应用中没什么毛病,但在web游览器中他其实和游览器行为是完全对立的。

标签模式和传统路由的冲突

传统路由如react-router之类的东西, 都是用过监听游览器路径来实现路由的, 所以路由入口是唯一的。
而标签模式下, 很难单纯通过url识别究竟是新建一个标签,还是在更新某个标签内部的页面内容, 需要通过用户行为来识别。

例子

操作1 用户点击菜单打开 /pageA
操作2 然后又点击菜单打开 /pageB
操作3 最后点击游览器返回的返回上一页
这时候,程序只能跟踪到url变成了/pageA, 在不知到用户操作情况下,无法判定究竟是要作出下列哪个行为

  1. 新建一个标签 内容为 /pageA (当用户行为是点击侧菜单栏)
  2. 将第二个标签内容替换为 /pageA (当用户行为是在操作2打开的标签栏内点击一个链接)
  3. 切换到第一个标签(当用的操作行为是点击第一个标签)
  4. 删除标签/pageB, 切换到标签/pageA (当用操作行为是点击游览器倒退,将程序状态切换到操作1完成之后的状态)

总结

正常点的web页面, 路由的入口仅仅是url, 而url本身没有上下文, 无状态, 所以不存在歧义,刷新,前进,后退不会造成问题。
但是标签模式没法仅仅从url上消除歧义,需要至少需要通过 用户操作+上下文, 而用户行为+上下文难以映射为url,所以不要指望用传统的路由来做标签模式的路由了。

自己写路由

下面是个简单的标签模式的实现,注意如果要用标签模式,就别指望用基于URL变化的传统路由了,乖乖自己写路由吧。

import PageA from "./PageA"
import PageA from "./PageA"
class App extends React.Component {
  state = { current: 0, pages: [] }
  uniqId = 0
  render(){
    return (
      <div>
        <aside>
           <div onClick={()=> this.create("Page A", PageA, { search: {id: "xxxxx"} })}>
               新建页面PageA
            </div>
           <div onClick={()=> this.create("Page B", PageB, { search: {id: "xxxxx"} })}>
              新建页面PageB
           </div>
        </aside>
        <nav>
            {this.pages.map((page, i)=> 
              <div onClick={()=> this.setState({current: i})}>
                {page.title}
               </div>
             )}
            </nav>
         {this.state.pages.map(({key, component, props}, index)=>(
             <div key={key} style={display: index === this.state.current ? "block": "none"}>
                <component {...props} 
                    createPage={this.createPage.bind(this)} 
                    changePage={this.replacePage.bind(this, index)}/>
             </div>
         ))}
      </div>
      )
  }
  createPage(title, component, props)  {
      this.setState({ pages: [
          ...this.state.pages, 
          { component, title, props, key: this.uniqId++ }], 
          current: this.state.pages.length,
       })
  }
  replacePage(index, title, component, props ){
      const pages = [...this.sate.pages];
      pages[index] = { key: this.uniqId++, component, title, props };
      this.setState({pages})
  }
}

牛逼一点的路由

用 redux, 或者 React.createContext 配合 高阶组件

#5

额,现在问题是pageA,pageB两个是同一个组件,只是路由不一样。为什么会这样呢,是因为需求要通过配置生成页面,也就是说通过参数不同,我在这个组件里渲染不同的dom。比如pageA这个页面有5个input,pageB页面有6个input。但实际上两个页面关联的组件都是同一个组件。

然后,我在页签下,同时打开这两个页面,由于用的是同一个组件,参数就被后打开的重置了。比如先打开pageA,再打开pageB,pageA的input也变成6个了。

#6

首先感谢回答,这么长一段。:innocent:

#7

加一个key,当切换标签的时候,让react强制重新生成组件

return (){
  return (
   <>
      <Nav></Nav>
      <Main  key={this.state.activePath} />
   </>
  )
}
#8

这个肯定不行的,不能刷新的。

不过问题已经解决了,是自己2了。两个页面渲染依赖的model是同一个,也就是说state是同一个,state变化就把所有页面都重现渲染了。现在对state拆一下,之前是state:[],现在是state:{key:state1,key:state2},每个页面都有一个唯一的key,也就有专属的state了。

仍然非常感谢您的搭理。祝您生活愉快。:innocent:

1 Like
#9

现在对state拆一下??什么意思啊,我也有需求啊

#10

我这边多路由的状态管理是自己用redux实现的,我之前的问题是多个路由对应了一个state,后来将多个路由的state分为多个,就解决了。至于状态管理如何用redux实现, 我这边没现成写好的答案,你可以百度下。

#11

谢谢,我这边用我的方法解决了,多页签的问题谢谢你

#12

谢谢你,我这边用我的方式,解决了。

#13

嗯嗯,解决就好

#14

你好大佬,我这边有个类似的需求。
前提
1.公司技术栈老旧,之前的项目都是后端直接用jsp写的,大部分管理后台类的项目都是iframe嵌入到页面,多标签页模式。
2.现在我用antdpro搭了一套管理后台,左侧菜单从后端获取,菜单对应的页面分两种:需要使用iframe嵌入的若干页面,我自己用组件写的有相应路由的页面。
3.由于老项目点击菜单不会改变url,所以iframe渲染的页面我把后端返回的路径拼接成完整的待嵌入页面链接,key放入到redux中。格式如下:

{
  path: {
    '1edb365':{
      path: 'viewlet=1&originid=2',
      frUrl: true
    }
  },
  activeKey: '1edb365'
}

其中,path对象存储着目前打开的标签页的信息,每个标签页都有唯一的key值,activeKey代表激活哪个标签页,frUrl是一个标识符若是需要iframe嵌入的则为true,否则为false需要跳转到对应路由。
问题
1.我在点击菜单项的时候会根据其后端返回的每一项菜单内部的frUrl值决定是否点击之后增加redux里的path项,并修改当前activeKey。这些iframe嵌入的页面都在路由/Iframe下进行渲染,路由对应组件接受props,拿到完整页面的url进行渲染。但是,我不知道如何渲染自己的路由页面。
2.自己的页面势必涉及到唯一的路由,如果要做到老项目的效果,点击任何菜单不会修改地址栏路径,又能区分自己页面还是iframe页面,该如何改造。