From ac4c6f08944d19c36cfb1e68170c56df39db6180 Mon Sep 17 00:00:00 2001 From: eternallycyf <969475322@qq.com> Date: Sun, 23 Apr 2023 23:51:26 +0800 Subject: [PATCH] =?UTF-8?q?feat=E2=9C=A8:=20=20add=20KeepAlive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++ src/core/Enhance/Authorized.tsx | 14 +++- src/core/base/KeepAlive/KeepAlive.tsx | 73 +++++++++++++++++++ src/core/base/KeepAlive/KeepAliveContext.tsx | 11 +++ src/core/base/KeepAlive/KeepAliveReducer.ts | 61 ++++++++++++++++ src/core/base/KeepAlive/KeepAliveTransfer.tsx | 35 +++++++++ src/core/base/KeepAlive/README.md | 24 ++++++ src/core/base/KeepAlive/actionTypes.ts | 2 + src/core/base/KeepAlive/index.tsx | 4 + src/core/base/Login/index.less | 4 +- src/core/layouts/BasicLayout.tsx | 12 ++- src/pages/Component/class/index.tsx | 2 + src/pages/Component/hook/index.tsx | 2 + src/pages/FileViewer/index.tsx | 11 ++- 14 files changed, 262 insertions(+), 9 deletions(-) create mode 100644 src/core/base/KeepAlive/KeepAlive.tsx create mode 100644 src/core/base/KeepAlive/KeepAliveContext.tsx create mode 100644 src/core/base/KeepAlive/KeepAliveReducer.ts create mode 100644 src/core/base/KeepAlive/KeepAliveTransfer.tsx create mode 100644 src/core/base/KeepAlive/README.md create mode 100644 src/core/base/KeepAlive/actionTypes.ts create mode 100644 src/core/base/KeepAlive/index.tsx diff --git a/README.md b/README.md index a635250..f28ae38 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - ~~docker 部署方案~~ - ~~封装虚拟列表,集成到 antd table 中~~ - 使用哈希重构数据格式 增加查询速度 +- 自己实现一个 keep-alive - 单元 测试 - ~~UI 自动化测试 puppeteer~~ - threejs 着色器 @@ -298,3 +299,18 @@ export default compose( layout, }, ``` + +## keepAlive + +```tsx | pure +import { KeepAlive } from '@/core/base/KeepAlive'; + +; + +export default compose( + withRoutePage, + withRouter, + connect(({ global, login }: ConnectState) => ({ token: login.token })), + KeepAliveTransfer, +)(Activity); +``` diff --git a/src/core/Enhance/Authorized.tsx b/src/core/Enhance/Authorized.tsx index 6199c75..2162385 100644 --- a/src/core/Enhance/Authorized.tsx +++ b/src/core/Enhance/Authorized.tsx @@ -4,6 +4,8 @@ import { History } from 'history'; import React, { useEffect } from 'react'; import { Navigate } from 'react-router-dom'; import { RouteComponentProps } from '@umijs/renderer-react'; +import { withRoutePage } from './withRoutePage'; +import { compose } from 'redux'; interface IProps extends RouteComponentProps { token: string; @@ -31,7 +33,11 @@ const Authorized: React.FC = (props) => { return ; }; -export default connect(({ login, global }: ConnectState) => ({ - token: login.token, - userInfo: global.userInfo, -}))(withRouter(Authorized)); +export default compose( + withRoutePage, + withRouter, + connect(({ global, login }: ConnectState) => ({ + token: login.token, + userInfo: global.userInfo, + })), +)(Authorized); diff --git a/src/core/base/KeepAlive/KeepAlive.tsx b/src/core/base/KeepAlive/KeepAlive.tsx new file mode 100644 index 0000000..9651c2f --- /dev/null +++ b/src/core/base/KeepAlive/KeepAlive.tsx @@ -0,0 +1,73 @@ +import React, { FC, useCallback, useReducer } from 'react'; +import { IKeepAliveContext, KeepAliveContext } from './KeepAliveContext'; +import { KeepAliveActions, keepAliveReducer, KeepAliveState } from './KeepAliveReducer'; +import * as actionTypes from './actionTypes'; + +interface IKeepAliveProps { + children: React.ReactNode; +} + +export type ISetKeepAliveState = (arg: KeepAliveActions['payload']) => void; + +/** + * { + * keepAliveId: 'class' + * reactElement: ReactElement + * nodes: Node[] + * status: 'CREATING' | 'CREATED' + * } + */ + +const KeepAlive: FC = (props) => { + const [keepAliveStates, dispatch] = useReducer(keepAliveReducer, {}); + + const setKeepAliveState: ISetKeepAliveState = useCallback( + ({ reactElement, keepAliveId }) => { + if (!keepAliveStates[keepAliveId]) { + dispatch({ + type: actionTypes.CREATING, + payload: { + keepAliveId, + reactElement, + }, + }); + } + }, + [keepAliveStates], + ); + + return ( + + {props.children} + {Object?.values(keepAliveStates).map((state) => { + const { keepAliveId, reactElement } = state; + return ( +
{ + if (node && !keepAliveStates[keepAliveId]?.nodes) { + dispatch({ + type: actionTypes.CREATED, + payload: { + keepAliveId, + nodes: Array.from(node.childNodes) as any[], + }, + }); + } + }} + > + {reactElement} +
+ ); + })} +
+ ); +}; + +export default KeepAlive; diff --git a/src/core/base/KeepAlive/KeepAliveContext.tsx b/src/core/base/KeepAlive/KeepAliveContext.tsx new file mode 100644 index 0000000..6d1c5a8 --- /dev/null +++ b/src/core/base/KeepAlive/KeepAliveContext.tsx @@ -0,0 +1,11 @@ +import { createContext } from 'react'; +import { ISetKeepAliveState } from './KeepAlive'; +import { KeepAliveActions, KeepAliveState } from './KeepAliveReducer'; + +export interface IKeepAliveContext { + keepAliveStates: KeepAliveState; + setKeepAliveState: ISetKeepAliveState; + dispatch: React.Dispatch; +} + +export const KeepAliveContext = createContext({} as IKeepAliveContext); diff --git a/src/core/base/KeepAlive/KeepAliveReducer.ts b/src/core/base/KeepAlive/KeepAliveReducer.ts new file mode 100644 index 0000000..9dee841 --- /dev/null +++ b/src/core/base/KeepAlive/KeepAliveReducer.ts @@ -0,0 +1,61 @@ +import * as actionTypes from './actionTypes'; + +export interface KeepAliveState { + [key: string]: { + keepAliveId: string; + reactElement?: React.ReactElement; + status: string; + nodes?: Array; + }; +} + +const initialState = {}; + +export type KeepAliveActions = + | { + type: 'CREATING'; + payload: { + keepAliveId: string; + reactElement?: React.ReactElement; + nodes?: Array; + }; + } + | { + type: 'CREATED'; + payload: { + keepAliveId: string; + reactElement?: React.ReactElement; + nodes?: Array; + }; + }; + +export function keepAliveReducer(state: KeepAliveState | any, action: KeepAliveActions): KeepAliveState { + const { type, payload } = action; + const { keepAliveId, reactElement, nodes } = payload; + switch (action.type) { + case actionTypes.CREATING: { + return { + ...state, + [keepAliveId]: { + keepAliveId, + reactElement, + status: type, + nodes: null, + }, + }; + } + case actionTypes.CREATED: { + return { + ...state, + [keepAliveId]: { + ...state[keepAliveId], + status: type, + nodes, + }, + }; + } + default: { + return state; + } + } +} diff --git a/src/core/base/KeepAlive/KeepAliveTransfer.tsx b/src/core/base/KeepAlive/KeepAliveTransfer.tsx new file mode 100644 index 0000000..3064f01 --- /dev/null +++ b/src/core/base/KeepAlive/KeepAliveTransfer.tsx @@ -0,0 +1,35 @@ +import React, { useContext, useEffect } from 'react'; +import { KeepAliveContext } from './KeepAliveContext'; + +interface IKeepAliveTransferProps { + KeepAliveComponent: React.ComponentType; + keepAliveId: string; +} + +const KeepAliveTransfer = ( + KeepAliveComponent: React.ComponentType | any, + keepAliveId?: string, +) => { + const displayName = KeepAliveComponent.displayName || KeepAliveComponent.name || keepAliveId; + + return (props: Omit) => { + const _ref = React.useRef(null!); + const { keepAliveStates, setKeepAliveState } = useContext(KeepAliveContext); + + useEffect(() => { + const state = keepAliveStates[displayName]; + if (state && state?.nodes) { + state.nodes.forEach((node: any) => _ref.current.appendChild(node)); + } else { + setKeepAliveState({ + keepAliveId: displayName, + reactElement: , + }); + } + }, [keepAliveStates, setKeepAliveState, props]); + + return
; + }; +}; + +export default KeepAliveTransfer; diff --git a/src/core/base/KeepAlive/README.md b/src/core/base/KeepAlive/README.md new file mode 100644 index 0000000..eb9e4cd --- /dev/null +++ b/src/core/base/KeepAlive/README.md @@ -0,0 +1,24 @@ +## Context + +- 将组件 dom 缓存保存在一个对象中 +- id: {nodes,ReactElement,status} + +## 结构 + +- Provider => 方法 属性 + - 组件 => nodes => 设置缓存的方法 组件的缓存 + +## use + +```tsx | pure +import { KeepAlive } from '@/core/base/KeepAlive'; + +; + +export default compose( + withRoutePage, + withRouter, + connect(({ global, login }: ConnectState) => ({ token: login.token })), + KeepAliveTransfer, +)(Activity); +``` diff --git a/src/core/base/KeepAlive/actionTypes.ts b/src/core/base/KeepAlive/actionTypes.ts new file mode 100644 index 0000000..1c65263 --- /dev/null +++ b/src/core/base/KeepAlive/actionTypes.ts @@ -0,0 +1,2 @@ +export const CREATING = 'CREATING'; +export const CREATED = 'CREATED'; diff --git a/src/core/base/KeepAlive/index.tsx b/src/core/base/KeepAlive/index.tsx new file mode 100644 index 0000000..fa7fa47 --- /dev/null +++ b/src/core/base/KeepAlive/index.tsx @@ -0,0 +1,4 @@ +import KeepAlive from './KeepAlive'; +import KeepAliveTransfer from './KeepAliveTransfer'; + +export { KeepAlive, KeepAliveTransfer }; diff --git a/src/core/base/Login/index.less b/src/core/base/Login/index.less index 29981ad..84366d4 100644 --- a/src/core/base/Login/index.less +++ b/src/core/base/Login/index.less @@ -1,6 +1,6 @@ .site-pro-form-login-container { position: fixed; - z-index: -999; + z-index: 999; display: grid; overflow: auto; width: 100%; @@ -36,4 +36,4 @@ font-family: -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji; line-height: 1.5714285714285714; -} +} \ No newline at end of file diff --git a/src/core/layouts/BasicLayout.tsx b/src/core/layouts/BasicLayout.tsx index 45d7cbd..ac8c230 100644 --- a/src/core/layouts/BasicLayout.tsx +++ b/src/core/layouts/BasicLayout.tsx @@ -5,7 +5,7 @@ import GlobalHeader from '@/core/base/GlobalHeader'; import TagsNav from '@/core/base/TagsNav'; import { ConnectState } from '@/typings/connect'; import { connect, Dispatch, getDvaApp, Helmet, History, Outlet, withRouter } from '@umijs/max'; -import { RouteComponentProps } from '@umijs/renderer-react'; +import { RouteComponentProps, useAppData, useLocation } from '@umijs/renderer-react'; import { ConfigProvider, Layout, Spin, theme as antdTheme, TourProps, Tour, FloatButton } from 'antd'; import _ from 'lodash'; import { BellOutlined } from '@ant-design/icons'; @@ -15,6 +15,8 @@ import { FC, Fragment, useEffect, useState, useRef } from 'react'; import ColorPicker from '../base/GlobalHeader/ColorPicker'; import styles from './index.less'; import IndexPage from './IndexPage'; +import { KeepAlive, KeepAliveTransfer } from '../base/KeepAlive'; + const { Sider, Content, Header } = Layout; const { title } = config; @@ -34,6 +36,7 @@ export interface IRgba { } const BasicLayout: FC = (props) => { + const { routes } = useAppData(); const [isDark, setIsDark] = useState(false); const [color, setColor] = useState({ r: '25', @@ -48,6 +51,9 @@ const BasicLayout: FC = (props) => { const ref3 = useRef(null!); const { menuList, breadcrumbNameMap, children, theme, collapsed, location, sliderMenuState, dispatch, userInfo } = props; + const currentRoutesObj = Object.values(routes).filter((item) => item?.path == location.pathname)?.[0]; + // TODO: routes 添加keepAlive配置 + // const hasKeepAlive = currentRoutesObj?.keepAlive; const steps: TourProps['steps'] = [ { @@ -161,7 +167,9 @@ const BasicLayout: FC = (props) => { - {location.pathname !== '/' ? : } + + {location.pathname !== '/' ? : } + diff --git a/src/pages/Component/class/index.tsx b/src/pages/Component/class/index.tsx index de68796..a83e2d0 100644 --- a/src/pages/Component/class/index.tsx +++ b/src/pages/Component/class/index.tsx @@ -19,6 +19,7 @@ import { getFieldComp } from '@/core/helpers'; import { withRoutePage } from '@/core/Enhance/withRoutePage'; import { compose } from 'redux'; import { ConnectState } from '@/typings/connect'; +import { KeepAliveTransfer } from '@/core/base/KeepAlive'; const { apiPrefixMock } = projectConfig; interface IProps {} @@ -253,4 +254,5 @@ export default compose( withRoutePage, withRouter, connect(({ global, login }: ConnectState) => ({ token: login.token })), + KeepAliveTransfer, )(Activity); diff --git a/src/pages/Component/hook/index.tsx b/src/pages/Component/hook/index.tsx index ea510e2..98499cd 100644 --- a/src/pages/Component/hook/index.tsx +++ b/src/pages/Component/hook/index.tsx @@ -22,6 +22,7 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { compose } from 'redux'; import _ from 'lodash'; import { withRouter } from '@/core/Enhance/withRouter'; +import { KeepAliveTransfer } from '@/core/base/KeepAlive'; interface IProps { IndexPageRef: Ref; @@ -257,6 +258,7 @@ const NewIndexPage = compose( pure: undefined, }), forwardRef, + KeepAliveTransfer, )(IndexPage); const Content = () => { diff --git a/src/pages/FileViewer/index.tsx b/src/pages/FileViewer/index.tsx index 6876a63..0c7c564 100644 --- a/src/pages/FileViewer/index.tsx +++ b/src/pages/FileViewer/index.tsx @@ -1,2 +1,11 @@ import FileView from '@/components/FileViewer/demo/demo'; -export default FileView; +import { KeepAliveTransfer } from '@/core/base/KeepAlive'; +import { withRoutePage } from '@/core/Enhance/withRoutePage'; +import { withRouter } from '@umijs/max'; +import { compose } from 'redux'; + +const IndexPage = () => { + return ; +}; + +export default compose(withRoutePage, withRouter, KeepAliveTransfer)(IndexPage);