umijs/umi

[Bug] 约定式路由无法生成嵌套路由!!

Pszz opened this issue · 18 comments

Pszz commented
  • 这是UMi3.x 的文档
    image

  • 最小化复现代码

  • src/pages/ui/_layout.tsx

import React from 'react';
import { Outlet } from 'umi';

export default function () => {
  return (
    <>
        <div>父层内容</div>
        <div>
          /* 无效 */
          <Outlet />
        </div>
    </>
  );
}

由于缺乏足够的信息,我们暂时关闭了该 Issue。请修改(不要回复) Issue 提供最小重现以重新开启。谢谢。

+1 !

而且这个 Outlet 组件感觉也不起作用

Pszz commented

而且这个 Outlet 组件感觉也不起作用

去掉约定式路由,就可以支持。

  • .umirc.ts
    { path: '/ui', component: 'ui', routes: [ { path: '/ui/button', component: 'ui/button', }, ] }

  • /pags/ui/index.tsx
    import { useOutlet } from 'umi'; export default () => { const outlet = useOutlet(); return <> <div>父层内容</div> <div>嵌套内容{ outlet }</div> </> }

而且这个 Outlet 组件感觉也不起作用

去掉约定式路由,就可以支持。

  • .umirc.ts
    { path: '/ui', component: 'ui', routes: [ { path: '/ui/button', component: 'ui/button', }, ] }
  • /pags/ui/index.tsx
    import { useOutlet } from 'umi'; export default () => { const outlet = useOutlet(); return <> <div>父层内容</div> <div>嵌套内容{ outlet }</div> </> }

但是我们项目太大了,一直都用的是约定式路由,现在改成配置式的,改动量太大了;

而且这个 Outlet 组件感觉也不起作用

去掉约定式路由,就可以支持。

  • .umirc.ts
    { path: '/ui', component: 'ui', routes: [ { path: '/ui/button', component: 'ui/button', }, ] }
  • /pags/ui/index.tsx
    import { useOutlet } from 'umi'; export default () => { const outlet = useOutlet(); return <> <div>父层内容</div> <div>嵌套内容{ outlet }</div> </> }

可以再打开这个 issue 吗?我看最小复现代码也有了的 @sorrycc

fz6m commented

现在没有 _layout 了,你需要的可能是:

./src/pages
├── nested
   └── a.tsx
└── nested.tsx
// nested.tsx

import { Outlet } from 'umi'

export default function N() {
  return (
    <div>
      wrapper :
      <Outlet />
    </div>
  )
}
// nested/a.tsx

export default function N() {
  return <div>aaa</div>
}

现在没有 _layout 了,你需要的可能是:

./src/pages
├── nested
   └── a.tsx
└── nested.tsx
// nested.tsx

import { Outlet } from 'umi'

export default function N() {
  return (
    <div>
      wrapper :
      <Outlet />
    </div>
  )
}
// nested/a.tsx

export default function N() {
  return <div>aaa</div>
}

这个我试了, 外层的 layout 的命名是有什么要求吗(都得是 nested ?)?我这么写之后, Outlet 拿到的属性是空的

fz6m commented

文件夹和当级路由的名字一样即可。

./src/pages
├── ${name}
   └── a.tsx
└── ${name}.tsx

好的,我试试哦,非常感谢!

以下是报错
image

我的目录结构如下:
image

另外,我还想问一下,如上所示的目录,如果我想要 projects 目录下的三个文件 metadata| resources | roles 都能有一个一样的 layout 文件,应该要怎么做呢

fz6m commented

给一个最小复现。

@fz6m
image
um4 里面 这个规则失效了 下划线开头的目录和文件也被注册为路由了

以下是报错 image

我的目录结构如下: image

另外,我还想问一下,如上所示的目录,如果我想要 projects 目录下的三个文件 metadata| resources | roles 都能有一个一样的 layout 文件,应该要怎么做呢

repo 我后面有时间了整一个;
请问第二个问题:「如果我想要 projects 目录下的三个文件 metadata| resources | roles 都能有一个一样的 layout 文件」 有什么解决方案吗

laeni commented

而且这个 Outlet 组件感觉也不起作用

去掉约定式路由,就可以支持。

  • .umirc.ts
    { path: '/ui', component: 'ui', routes: [ { path: '/ui/button', component: 'ui/button', }, ] }
  • /pags/ui/index.tsx
    import { useOutlet } from 'umi'; export default () => { const outlet = useOutlet(); return <> <div>父层内容</div> <div>嵌套内容{ outlet }</div> </> }

但是我们项目太大了,一直都用的是约定式路由,现在改成配置式的,改动量太大了;

我们的情况和你们差不多,不过我们的情况是子路由下一般会有一个index.tsx页面,用<Outlet />和这下面种方式的话,会导致${name}.tsx的优先级高于${name}/index.tsx而导致index.tsx不生效。

./src/pages
├── ${name}
│   ├── index.tsx
│   └── a.tsx
└── ${name}.tsx

我们的解决方案如下,你可以参考下:在_layout.tsx所在的目录下创建components目录,并将_layout.tsx移动到该目录下,这样自动生成的路由就会忽略掉_layout.tsx;然后将每一个子页面都包裹在_layout.tsx组件下。

./src/pages
└── ${name}
    ├── components
    │   └── _layout.tsx
    ├── index.tsx
    └── a.tsx

index.tsx and a.tsx

import Layout from './components/_layout';

export default function TestPage() {
    return (
        <Layout>
            // 原本的子组件内容
        </Layout>
    )
}

而且这个 Outlet 组件感觉也不起作用

去掉约定式路由,就可以支持。

  • .umirc.ts
    { path: '/ui', component: 'ui', routes: [ { path: '/ui/button', component: 'ui/button', }, ] }
  • /pags/ui/index.tsx
    import { useOutlet } from 'umi'; export default () => { const outlet = useOutlet(); return <> <div>父层内容</div> <div>嵌套内容{ outlet }</div> </> }

但是我们项目太大了,一直都用的是约定式路由,现在改成配置式的,改动量太大了;

我们的情况和你们差不多,不过我们的情况是子路由下一般会有一个index.tsx页面,用<Outlet />和这下面种方式的话,会导致${name}.tsx的优先级高于${name}/index.tsx而导致index.tsx不生效。

./src/pages
├── ${name}
│   ├── index.tsx
│   └── a.tsx
└── ${name}.tsx

我们的解决方案如下,你可以参考下:在_layout.tsx所在的目录下创建components目录,并将_layout.tsx移动到该目录下,这样自动生成的路由就会忽略掉_layout.tsx;然后将每一个子页面都包裹在_layout.tsx组件下。

./src/pages
└── ${name}
    ├── components
    │   └── _layout.tsx
    ├── index.tsx
    └── a.tsx

index.tsx and a.tsx

import Layout from './components/_layout';

export default function TestPage() {
    return (
        <Layout>
            // 原本的子组件内容
        </Layout>
    )
}

非常感谢你的解答和方案!我后面就是这么做的,但是这样有一个点不太好,就是layout里的内容一般是公用的对吧,就比如我 detail 的页面里有四个 tab 页,每次进到一个 tab(对应不同路由),layout 里的内容就会刷新一次,而实际上这些内容第一次进来的时候就应该已经渲染好了的,不知道我有没有讲明白

同样的问题,约定式路由下,umi4没有办法使用umi3 _layout嵌套路由的功能。

fz6m commented

目前 umi 3 升 umi 4 需要自己处理下 layout 的问题,主要关心两个变化:

第一是 umi 4 是用的 react router v6 ,嵌套路由的出口是 <Outlet /> ,当使用嵌套路由的时候,需要在展示子嵌套内容的位置加上 <Outlet /> (从 umi 导入),这样才能有子级内容。

第二是 umi 4 里没有 _layout ,可以自己改代码把 <Layout></Layout> 套上或者使用 wrappers 配置,另外也可以在运行时 patch 路由加上 layout 。