mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
⚡️ perf: make app page as static route to improve performance (#5478)
* move files * refactor to static mode * fix tests
This commit is contained in:
@@ -176,6 +176,11 @@ const nextConfig: NextConfig = {
|
||||
permanent: false,
|
||||
source: '/repos',
|
||||
},
|
||||
{
|
||||
destination: '/files',
|
||||
permanent: false,
|
||||
source: '/repos',
|
||||
},
|
||||
],
|
||||
// when external packages in dev mode with turbopack, this config will lead to bundle error
|
||||
serverExternalPackages: isProd ? ['@electric-sql/pglite'] : undefined,
|
||||
|
||||
+1
-1
@@ -317,7 +317,7 @@
|
||||
"vitest": "~1.2.2",
|
||||
"vitest-canvas-mock": "^0.3.3"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.4",
|
||||
"packageManager": "pnpm@10.2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
+7
-4
@@ -4,7 +4,8 @@ import { Center } from 'react-layout-kit';
|
||||
import BrandWatermark from '@/components/BrandWatermark';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import Category from './features/Category';
|
||||
import UserBanner from './features/UserBanner';
|
||||
@@ -17,10 +18,10 @@ export const generateMetadata = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const Page = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
if (!mobile) return redirect('/chat');
|
||||
if (!isMobile) return redirect('/chat');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -36,3 +37,5 @@ const Page = async () => {
|
||||
Page.displayName = 'Me';
|
||||
|
||||
export default Page;
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
+1
-1
@@ -9,7 +9,7 @@ import Cell, { CellProps } from '@/components/Cell';
|
||||
import { isDeprecatedEdition } from '@/const/version';
|
||||
import { ProfileTabs } from '@/store/global/initialState';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { authSelectors } from '@/store/user/slices/auth/selectors';
|
||||
import { authSelectors } from '@/store/user/selectors';
|
||||
|
||||
const Category = memo(() => {
|
||||
const [isLogin, enableAuth, isLoginWithClerk, signOut] = useUserStore((s) => [
|
||||
+7
-4
@@ -2,7 +2,8 @@ import { redirect } from 'next/navigation';
|
||||
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import Category from './features/Category';
|
||||
|
||||
@@ -15,10 +16,10 @@ export const generateMetadata = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const Page = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
if (!mobile) return redirect('/profile');
|
||||
if (!isMobile) return redirect('/profile');
|
||||
|
||||
return <Category />;
|
||||
};
|
||||
@@ -26,3 +27,5 @@ const Page = async () => {
|
||||
Page.displayName = 'MeProfile';
|
||||
|
||||
export default Page;
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
+7
-4
@@ -2,7 +2,8 @@ import { redirect } from 'next/navigation';
|
||||
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import Category from './features/Category';
|
||||
|
||||
@@ -15,10 +16,10 @@ export const generateMetadata = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const Page = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
if (!mobile) return redirect('/settings/common');
|
||||
if (!isMobile) return redirect('/settings/common');
|
||||
|
||||
return <Category />;
|
||||
};
|
||||
@@ -26,3 +27,5 @@ const Page = async () => {
|
||||
Page.displayName = 'MeSettings';
|
||||
|
||||
export default Page;
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
+11
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
import { SideNav } from '@lobehub/ui';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { memo } from 'react';
|
||||
import { Suspense, memo } from 'react';
|
||||
|
||||
import { useActiveTabKey } from '@/hooks/useActiveTabKey';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
@@ -14,11 +14,16 @@ import BottomActions from './BottomActions';
|
||||
import PinList from './PinList';
|
||||
import TopActions from './TopActions';
|
||||
|
||||
const Nav = memo(() => {
|
||||
const Top = () => {
|
||||
const [isPinned] = useQueryState('pinned', parseAsBoolean);
|
||||
const sidebarKey = useActiveTabKey();
|
||||
|
||||
return <TopActions isPinned={isPinned} tab={sidebarKey} />;
|
||||
};
|
||||
|
||||
const Nav = memo(() => {
|
||||
const inZenMode = useGlobalStore(systemStatusSelectors.inZenMode);
|
||||
const { showPinList } = useServerConfigStore(featureFlagsSelectors);
|
||||
const [isPinned] = useQueryState('pinned', parseAsBoolean);
|
||||
|
||||
return (
|
||||
!inZenMode && (
|
||||
@@ -27,10 +32,10 @@ const Nav = memo(() => {
|
||||
bottomActions={<BottomActions />}
|
||||
style={{ height: '100%', zIndex: 100 }}
|
||||
topActions={
|
||||
<>
|
||||
<TopActions isPinned={isPinned} tab={sidebarKey} />
|
||||
<Suspense>
|
||||
<Top />
|
||||
{showPinList && <PinList />}
|
||||
</>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
)
|
||||
+3
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import qs from 'query-string';
|
||||
import { PropsWithChildren, memo } from 'react';
|
||||
|
||||
import { withSuspense } from '@/components/withSuspense';
|
||||
import { useShowMobileWorkspace } from '@/hooks/useShowMobileWorkspace';
|
||||
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
|
||||
@@ -24,8 +24,7 @@ const MOBILE_NAV_ROUTES = new Set([
|
||||
const Layout = memo(({ children }: PropsWithChildren) => {
|
||||
const showMobileWorkspace = useShowMobileWorkspace();
|
||||
const pathname = usePathname();
|
||||
const { url } = qs.parseUrl(pathname);
|
||||
const showNav = !showMobileWorkspace && MOBILE_NAV_ROUTES.has(url);
|
||||
const showNav = !showMobileWorkspace && MOBILE_NAV_ROUTES.has(pathname);
|
||||
|
||||
const { showCloudPromotion } = useServerConfigStore(featureFlagsSelectors);
|
||||
|
||||
@@ -40,4 +39,4 @@ const Layout = memo(({ children }: PropsWithChildren) => {
|
||||
|
||||
Layout.displayName = 'MobileMainLayout';
|
||||
|
||||
export default Layout;
|
||||
export default withSuspense(Layout);
|
||||
+3
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import { withSuspense } from '@/components/withSuspense';
|
||||
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
||||
|
||||
/**
|
||||
@@ -10,7 +11,7 @@ import { useQueryRoute } from '@/hooks/useQueryRoute';
|
||||
* @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
|
||||
*/
|
||||
|
||||
const ChangelogModalFallback = () => {
|
||||
const ChangelogModal = () => {
|
||||
const router = useQueryRoute();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -20,4 +21,4 @@ const ChangelogModalFallback = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ChangelogModalFallback;
|
||||
export default withSuspense(ChangelogModal);
|
||||
@@ -4,7 +4,7 @@ import { Fragment, Suspense } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import Pagination from '@/app/@modal/(.)changelog/modal/features/Pagination';
|
||||
import Pagination from '@/app/[variants]/@modal/(.)changelog/modal/features/Pagination';
|
||||
import StructuredData from '@/components/StructuredData';
|
||||
import { serverFeatureFlags } from '@/config/featureFlags';
|
||||
import { BRANDING_NAME } from '@/const/branding';
|
||||
@@ -13,7 +13,8 @@ import { ldModule } from '@/server/ld';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { ChangelogService } from '@/server/services/changelog';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import GridLayout from './features/GridLayout';
|
||||
import Post from './features/Post';
|
||||
@@ -28,12 +29,12 @@ export const generateMetadata = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const Page = async (props: DynamicLayoutProps) => {
|
||||
const hideDocs = serverFeatureFlags().hideDocs;
|
||||
|
||||
if (hideDocs) return notFound();
|
||||
|
||||
const mobile = await isMobileDevice();
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
const { t, locale } = await translation('metadata');
|
||||
const changelogService = new ChangelogService();
|
||||
const data = await changelogService.getChangelogIndex();
|
||||
@@ -49,7 +50,7 @@ const Page = async () => {
|
||||
return (
|
||||
<>
|
||||
<StructuredData ld={ld} />
|
||||
<Flexbox gap={mobile ? 16 : 48}>
|
||||
<Flexbox gap={isMobile ? 16 : 48}>
|
||||
{data?.map((item) => (
|
||||
<Fragment key={item.id}>
|
||||
<Suspense
|
||||
@@ -60,7 +61,7 @@ const Page = async () => {
|
||||
</GridLayout>
|
||||
}
|
||||
>
|
||||
<Post locale={locale} mobile={mobile} {...item} />
|
||||
<Post locale={locale} mobile={isMobile} {...item} />
|
||||
</Suspense>
|
||||
</Fragment>
|
||||
))}
|
||||
+6
-5
@@ -1,4 +1,5 @@
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import ChatHydration from './features/ChatHydration';
|
||||
import ChatInput from './features/ChatInput';
|
||||
@@ -6,14 +7,14 @@ import ChatList from './features/ChatList';
|
||||
import ThreadHydration from './features/ThreadHydration';
|
||||
import ZenModeToast from './features/ZenModeToast';
|
||||
|
||||
const ChatConversation = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const ChatConversation = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ZenModeToast />
|
||||
<ChatList mobile={mobile} />
|
||||
<ChatInput mobile={mobile} />
|
||||
<ChatList mobile={isMobile} />
|
||||
<ChatInput mobile={isMobile} />
|
||||
<ChatHydration />
|
||||
<ThreadHydration />
|
||||
</>
|
||||
+5
-4
@@ -1,17 +1,18 @@
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
|
||||
import Loading from '@/components/Loading/BrandTextLoading';
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import Desktop from './_layout/Desktop';
|
||||
import Mobile from './_layout/Mobile';
|
||||
|
||||
const PortalBody = lazy(() => import('@/features/Portal/router'));
|
||||
|
||||
const Inspector = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const Inspector = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
const Layout = mobile ? Mobile : Desktop;
|
||||
const Layout = isMobile ? Mobile : Desktop;
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Loading />}>
|
||||
+6
-5
@@ -1,7 +1,8 @@
|
||||
// import TopicListContent from './features/TopicListContent';
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
|
||||
import { isMobileDevice } from '@/utils/server/responsive';
|
||||
import { DynamicLayoutProps } from '@/types/next';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import Desktop from './_layout/Desktop';
|
||||
import Mobile from './_layout/Mobile';
|
||||
@@ -10,14 +11,14 @@ import SystemRole from './features/SystemRole';
|
||||
|
||||
const TopicContent = lazy(() => import('./features/TopicListContent'));
|
||||
|
||||
const Topic = async () => {
|
||||
const mobile = await isMobileDevice();
|
||||
const Topic = async (props: DynamicLayoutProps) => {
|
||||
const isMobile = await RouteVariants.getIsMobile(props);
|
||||
|
||||
const Layout = mobile ? Mobile : Desktop;
|
||||
const Layout = isMobile ? Mobile : Desktop;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!mobile && <SystemRole />}
|
||||
{!isMobile && <SystemRole />}
|
||||
<Layout>
|
||||
<Suspense fallback={<SkeletonList />}>
|
||||
<TopicContent />
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user