Динамические компоненты
Наиболее подходящей демонстрацией будет пример с Layout - компонентом, изменяющим раскладку и общий дизайн приложения в зависимости от текущей страницы.
При использовании реактивных подходов есть три основных способа работы с динамическими компонентами:
- Вне компонента Router, используя
router.activeName
tsx
import { LayoutLogin } from 'layouts/LayoutLogin'
import { LayoutAuthZone } from 'layouts/LayoutAuthZone'
function App() {
const { router } = useRouter();
const Layout = ['login', 'restore'].includes(router.activeName!)
? LayoutLogin
: LayoutAuthZone;
return (
<Layout>
<Router router={router} />
</Layout>
);
}tsx
import { LayoutLogin } from 'layouts/LayoutLogin'
import { LayoutAuthZone } from 'layouts/LayoutAuthZone'
function App() {
const { router } = useRouter();
const Layout = ['login', 'restore'].includes(router.activeName!)
? LayoutLogin
: LayoutAuthZone;
return (
<Layout>
<Router router={router} />
</Layout>
);
}tsx
import { LayoutLogin } from 'layouts/LayoutLogin'
import { LayoutAuthZone } from 'layouts/LayoutAuthZone'
function App() {
const { router } = useRouter();
return (
<Dynamic
component={['login', 'restore'].includes(router.activeName!)
? LayoutLogin
: LayoutAuthZone
}
>
<Router router={router} />
</Dynamic>
);
}vue
<script lang="ts" setup>
import { computed } from 'vue';
import { useRouter } from '../../router';
import LayoutLogin from 'layouts/LayoutLogin.vue'
import LayoutAuthZone from 'layouts/LayoutAuthZone.vue'
const { router } = useRouter();
const Layout = computed(() =>
['login', 'restore'].includes(router.activeName!)
? LayoutLogin
: LayoutAuthZone
);
</script>
<template>
<component :is="Layout">
<Router :router="router" />
</component>
</template>- Внутри компонента страницы, реагируя на динамические параметры
ts
createConfigs({
dashboard: {
path: '/dashboard/:tab',
params: {
tab: (value) => ['table', 'widgets', 'charts'].includes(value)
},
query: {
editMode: (value) => ['0', '1', '2'].includes(value)
},
loader: () => import('./pages/dashboard'),
}
// other Configs
});tsx
export default function PageDashboard() {
const { router } = useContext(RouterContext);
const pageState = router.state.dashboard!;
return (
<Layout editMode={pageState.query.editMode || '0'}>
{pageState.params.tab === 'table' && <Table />}
{pageState.params.tab === 'widgets' && <Widgets />}
{pageState.params.tab === 'charts' && <Charts />}
</Layout>
);
}tsx
export default function PageDashboard() {
const { router } = useContext(RouterContext);
const pageState = router.state.dashboard!;
return (
<Layout editMode={pageState.query.editMode || '0'}>
{pageState.params.tab === 'table' && <Table />}
{pageState.params.tab === 'widgets' && <Widgets />}
{pageState.params.tab === 'charts' && <Charts />}
</Layout>
);
}tsx
export default function PageDashboard() {
const { router } = useContext(RouterContext);
const pageState = () => router.state.dashboard!;
return (
<Layout editMode={pageState().query.editMode || '0'}>
<Switch>
<Match when={pageState().params.tab === 'table'}><Table /></Match>
<Match when={pageState().params.tab === 'widgets'}><Widgets /></Match>
<Match when={pageState().params.tab === 'charts'}><Charts /></Match>
</Switch>
</Layout>
);
}vue
<script lang="ts" setup>
import { useRouterStore } from './router';
const { router } = useRouterStore();
const pageState = router.state.dashboard!;
</script>
<template>
<Layout :edit-mode="pageState.query.editMode || '0'">
<Table v-if="pageState.params.tab === 'table'" />
<Widgets v-else-if="pageState.params.tab === 'widgets'" />
<Charts v-else-if="pageState.params.tab === 'charts'" />
</Layout>
</template>- Установкой одинакового
loaderдля несколькихConfig. В этом случаеbeforeComponentChangeне будет вызываться, однакоrouter.activeNameбудет меняться. Этот пример мог бы выглядеть как предыдущие, но выберем подход с реактивной функцией:
ts
createConfigs({
dashboard: {
path: '/dashboard',
loader: () => import('./pages/dashboard'),
},
dashboardEdit: {
path: '/dashboard/edit',
loader: () => import('./pages/dashboard'),
},
dashboardAggregate: {
path: '/dashboard/aggregate',
loader: () => import('./pages/dashboard'),
}
});tsx
// with mobx adapter
import { autorun } from 'mobx'
function PageUser() {
const { router } = useContext(RouterContext);
const [ Layout, setLayout ] = useState();
useEffect(() => autorun(() => {
if (router.activeName === 'dashboard') {
setLayout(View)
} else if (router.activeName === 'dashboardEdit') {
setLayout(Edit)
} else if (router.activeName === 'dashboardAggregate') {
setLayout(Aggregate)
}
}), []);
if (!Layout) return null;
return <Layout>Content</Layout>;
}tsx
// with kr-observable adapter
import { autorun } from 'kr-observable'
function PageUser() {
const { router } = useContext(RouterContext);
const [ Layout, setLayout ] = useState();
useEffect(() => autorun(() => {
if (router.activeName === 'dashboard') {
setLayout(View)
} else if (router.activeName === 'dashboardEdit') {
setLayout(Edit)
} else if (router.activeName === 'dashboardAggregate') {
setLayout(Aggregate)
}
}), []);
if (!Layout) return null;
return <Layout>Content</Layout>;
}tsx
// with solid adapter
import { createMemo } from 'solid-js'
function PageUser() {
const { router } = useContext(RouterContext);
const Layout = createMemo(() => {
if (router.activeName === 'dashboard') return View;
if (router.activeName === 'dashboardEdit') return Edit;
if (router.activeName === 'dashboardAggregate') return Aggregate;
return null;
});
return (
<Show when={Layout()}>
{(Component) => <Component>Content</Component>}
</Show>
);
}vue
<script lang="ts" setup>
// with vue adapter
import { computed } from 'vue';
import { useRouterStore } from './router';
const { router } = useRouterStore();
const Layout = computed(() => {
if (router.activeName === 'dashboard') return View;
if (router.activeName === 'dashboardEdit') return Edit;
if (router.activeName === 'dashboardAggregate') return Aggregate;
return null;
});
</script>
<template>
<component :is="Layout" v-if="Layout">
Content
</component>
</template>Эти подходы можно комбинировать в зависимости от задачи.