路由
路由信息,在/packages/app/client/.umirc.ts中,只有一个路由地址。
routes: [{ path: '/', exact: false, component: '@/pages/index' }],
它的extac=false表明nocobase是所有的路由地址都经过@/pages/index这个组件,便获取不到更多的信息了。
在/packages/core/client/src/application/Application.tsx,通过Application类下面的代码
this.use(RemoteRouteSwitchProvider, {
  components: {
    AuthLayout,
    AdminLayout,
    RouteSchemaComponent,
    SigninPage,
    SignupPage,
    BlockTemplatePage,
    BlockTemplateDetails,
  },
});
找到RemoteRouteSwitchProvider代码
export function RemoteRouteSwitchProvider(props: RouteSwitchProviderProps) {
  const { data, loading } = useRequest({
    url: 'uiRoutes:getAccessible',
  });
  if (loading) {
    return <Spin />;
  }
  return <RouteSwitchProvider {...props} routes={data?.data || []} />;
}
上面获取到的data数据

由此可以确定,这个函数就是获取路由的代码。
继续往下看,走到Application的render函数的return
return (
  <ErrorBoundary FallbackComponent={ErrorFallback} onError={this.handleErrors}>
    <App providers={this.providers} />
  </ErrorBoundary>
);
再来看 App 函数的代码
const App = React.memo((props: any) => {
  const C = compose(...props.providers)(() => {
    const routes = useRoutes();
    return (
      <div>
        <RouteSwitch routes={routes} />
      </div>
    );
  });
  return <C />;
});
在看RouteSwitch可以看到它引用了react-router-dom,版本使用是"react-router-dom": "^5.2.0"
function RouteSwitch() {
    <Switch>
      {routes.map((route, index) => {
        if (route.type == 'redirect') {
          return (
            <Redirect
              ...
            />
          );
        }
        if (!route.path && Array.isArray(route.routes)) {
          route.path = route.routes.map((r) => r.path) as any;
        }
        return (
          <Route
                   ...
            render={(props) => {
              return (
                <RouteContext.Provider value={route}>
                  <ComponentRenderer {...props} route={route} />
                </RouteContext.Provider>
              );
            }}
          />
        );
      })}
    </Switch>
}
从上面的代码可以看出,它的当route的type=redirect的时候使用Redirect,其他使用Route
Redirect 组件介绍
类似于 HTTP 状态码 3xx,直接跳转。参数信息如下
to: string; // redirect到的地址
push: boolean; // true表示,redirect后会有历史记录
from: string; // 匹配需要redirect的地址
exact: boolean; // 是否精准匹配
strict: boolean; // 匹配路由后面的/,true表示/也必须匹配
在看回我们之前在控制台输出的 data,发现当路由是/,跳转到/admin

上面代码在他的渲染Route之前,有一个判断
if (!route.path && Array.isArray(route.routes)) {
  route.path = route.routes.map((r) => r.path) as any;
}
通过 console 可以发现,他的singin和signup渲染的一个 ComponentRenderer

继续看下去会发现ComponentRenderer实际上也是调用的RouteSwitch
<Component {...props}>
  <RouteSwitch routes={props.route.routes} />
</Component>
所以他实际上就是一个递归,最后 path=array 还是在重复走一遍 routes.map,signin和 signup,最后还是单独渲染的自己组件

第一次routes.map的return的是一个['/signin', '/signup'],通过递归第二次渲染routes.map,signin渲染的就是SinginPage,signup渲染的是SingnuPage