Merge pull request #173 from Worklenz/imp/task-list-performance-fixes
feat(lazy-loading): implement lazy loading and suspense for improved …
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
<title>Worklenz</title>
|
<title>Worklenz</title>
|
||||||
|
|
||||||
<!-- Environment configuration -->
|
<!-- Environment configuration -->
|
||||||
<script src="/env-config.js"></script>
|
<script type="module" src="/env-config.js"></script>
|
||||||
<!-- Unregister service worker -->
|
<!-- Unregister service worker -->
|
||||||
<script src="/unregister-sw.js"></script>
|
<script src="/unregister-sw.js"></script>
|
||||||
<!-- Microsoft Clarity -->
|
<!-- Microsoft Clarity -->
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import React from 'react';
|
import React, { lazy, Suspense } from 'react';
|
||||||
import { RouteObject } from 'react-router-dom';
|
import { RouteObject } from 'react-router-dom';
|
||||||
import NotFoundPage from '@/pages/404-page/404-page';
|
import { SuspenseFallback } from '@/components/suspense-fallback/suspense-fallback';
|
||||||
|
|
||||||
|
const NotFoundPage = lazy(() => import('@/pages/404-page/404-page'));
|
||||||
|
|
||||||
const notFoundRoute: RouteObject = {
|
const notFoundRoute: RouteObject = {
|
||||||
path: '*',
|
path: '*',
|
||||||
element: <NotFoundPage />,
|
element: (
|
||||||
|
<Suspense fallback={<SuspenseFallback />}>
|
||||||
|
<NotFoundPage />
|
||||||
|
</Suspense>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default notFoundRoute;
|
export default notFoundRoute;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
import { useEffect, useState, useRef, useMemo, useCallback, lazy, Suspense } from 'react';
|
||||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||||
import TaskListFilters from '../taskList/task-list-filters/task-list-filters';
|
|
||||||
|
const TaskListFilters = lazy(() => import('../taskList/task-list-filters/task-list-filters'));
|
||||||
import { Flex, Skeleton } from 'antd';
|
import { Flex, Skeleton } from 'antd';
|
||||||
import BoardSectionCardContainer from './board-section/board-section-container';
|
import BoardSectionCardContainer from './board-section/board-section-container';
|
||||||
import {
|
import {
|
||||||
@@ -553,7 +554,9 @@ const ProjectViewBoard = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex vertical gap={16}>
|
<Flex vertical gap={16}>
|
||||||
|
<Suspense fallback={<div>Loading filters...</div>}>
|
||||||
<TaskListFilters position={'board'} />
|
<TaskListFilters position={'board'} />
|
||||||
|
</Suspense>
|
||||||
<Skeleton active loading={isLoading} className='mt-4 p-4'>
|
<Skeleton active loading={isLoading} className='mt-4 p-4'>
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo, lazy, Suspense } from 'react';
|
||||||
import Flex from 'antd/es/flex';
|
import Flex from 'antd/es/flex';
|
||||||
import Skeleton from 'antd/es/skeleton';
|
import Skeleton from 'antd/es/skeleton';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import TaskListFilters from './task-list-filters/task-list-filters';
|
const TaskListFilters = lazy(() => import('./task-list-filters/task-list-filters'));
|
||||||
import TaskGroupWrapperOptimized from './task-group-wrapper-optimized';
|
import TaskGroupWrapperOptimized from './task-group-wrapper-optimized';
|
||||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||||
@@ -100,7 +100,9 @@ const ProjectViewTaskList = () => {
|
|||||||
return (
|
return (
|
||||||
<Flex vertical gap={16} style={{ overflowX: 'hidden' }}>
|
<Flex vertical gap={16} style={{ overflowX: 'hidden' }}>
|
||||||
{/* Filters load independently and don't block the main content */}
|
{/* Filters load independently and don't block the main content */}
|
||||||
|
<Suspense fallback={<div>Loading filters...</div>}>
|
||||||
<TaskListFilters position="list" />
|
<TaskListFilters position="list" />
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
{isEmptyState ? (
|
{isEmptyState ? (
|
||||||
<Empty description="No tasks group found" />
|
<Empty description="No tasks group found" />
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
output: {
|
output: {
|
||||||
// **Optimized Chunking Strategy**
|
// **Optimized Chunking Strategy**
|
||||||
manualChunks(id) {
|
manualChunks(id) {
|
||||||
// Core React libraries
|
// Core React libraries - keep together to avoid dependency issues
|
||||||
if (id.includes('react') || id.includes('react-dom')) {
|
if (id.includes('react') || id.includes('react-dom')) {
|
||||||
return 'react-vendor';
|
return 'react-vendor';
|
||||||
}
|
}
|
||||||
@@ -82,16 +82,11 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
return 'react-router';
|
return 'react-router';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ant Design (keep separate for better caching)
|
// Ant Design and Icons together to share React context
|
||||||
if (id.includes('antd') && !id.includes('@ant-design/icons')) {
|
if (id.includes('antd') || id.includes('@ant-design/icons')) {
|
||||||
return 'antd';
|
return 'antd';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icons (if using ant design icons)
|
|
||||||
if (id.includes('@ant-design/icons')) {
|
|
||||||
return 'antd-icons';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internationalization
|
// Internationalization
|
||||||
if (id.includes('i18next')) {
|
if (id.includes('i18next')) {
|
||||||
return 'i18n';
|
return 'i18n';
|
||||||
@@ -124,6 +119,9 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
|
|
||||||
// **External dependencies (if any should be externalized)**
|
// **External dependencies (if any should be externalized)**
|
||||||
external: [],
|
external: [],
|
||||||
|
|
||||||
|
// **Preserve modules to avoid context issues**
|
||||||
|
preserveEntrySignatures: 'strict',
|
||||||
},
|
},
|
||||||
|
|
||||||
// **Experimental features for better performance**
|
// **Experimental features for better performance**
|
||||||
@@ -138,10 +136,15 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
include: [
|
include: [
|
||||||
'react',
|
'react',
|
||||||
'react-dom',
|
'react-dom',
|
||||||
|
'react/jsx-runtime',
|
||||||
|
'antd',
|
||||||
|
'@ant-design/icons',
|
||||||
],
|
],
|
||||||
exclude: [
|
exclude: [
|
||||||
// Add any packages that should not be pre-bundled
|
// Add any packages that should not be pre-bundled
|
||||||
],
|
],
|
||||||
|
// Force pre-bundling to avoid runtime issues
|
||||||
|
force: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// **Define global constants**
|
// **Define global constants**
|
||||||
|
|||||||
Reference in New Issue
Block a user