利用CSS实现页面切换时保留组件

456 阅读2分钟

简介

问题

React中使用Redux实现高德地图AMap地图实例的缓存和复用一文中,为了避免多次加载地图影响用户体验,采用了Redux进行实例缓存。然而,在后续开发中发现,由于Redux存储的对象必须序列化,而高德地图的实例是不可序列化的。强行存储导致一些功能无法正常使用,例如设置点击事件。

在网上寻找多种解决方案,发现大多数要求存储项可序列化。

解决方案

后来找到了一个简单而有效的办法:将需要缓存的组件从router中独立出来,直接写在父组件中。通过修改CSS的display属性,根据路由path实现组件的隐藏和显示。这种方法的优势在于,路由跳转时,组件并没有被卸载,而是被隐藏。因此,当用户返回该页面时,无需等待组件重新加载。

同理,开发中当遇到某些组件加载需要较长时间且使用频率较高时,使用这种方法,可以有效减少等待时间,提高用户体验。

原理

CSS提供了多个属性来实现组件的隐藏,如下图所示: image.png

(图片来源:截图——css隐藏元素的六种方法_css hidden-CSDN博客)

通过使用`display: none`属性,可以隐藏元素而不占据空间,实现与使用路由的相似效果。

实现步骤

  1. 修改路由文件使用<></>代替<HidComponent />
import { type RouteObject } from 'react-router-dom'
import Layout from 'your_path_to/layout.tsx'
import HidComponent from 'your_path_to/HidComponent.tsx'
import OtherComponent from 'your_path_to/OtherComponent.tsx'

const AppRoutes: RouteObject[] = [
  {
    path: '/',
    element: <Layout />,
    children: [
      {
        path: '/HidComponent',
        element: <></>
      },
      {
        path: '/OtherComponent',
        element: <OtherComponent />
      }
    ]
  },
  {
    path: 'OtherComponent',
    element: <OtherComponent />
  }
]

export default AppRoutes
  1. 修改layout.tsx,将<HidComponent />直接放到组件原本被路由渲染到的位置,在我的项目中就是<Outlet />的上方
import React from 'react'
import { Outlet } from 'react-router-dom'
import HidComponent from 'your_path_to/HidComponent'

const App: React.FC = () => {
  // DOM
  return (
    <Layout>
      <Header >
        ......
      </Header>
      <Content style={{ height: '100%', width: '100%' }}>
        ......
        // 需要被保留的的组件
        <HidComponent />
        // 路由占位
        <Outlet />
        ......
      </Content>
    </Layout>
  )
}

export default App

  1. 新建一个CSS类.hide
.hide {
    display: none;
}
  1. 修改<HidComponent />,获取当前url,当路径不符时应用.hide类,隐藏组件
import { useEffect, useMemo } from 'react'
import { useLocation } from 'react-router-dom'
import styles from './styles.module.css'

const HidComponent: React.FC = () => {
  // 控制地图隐藏
  const url = useLocation()
  const hidden = useMemo(() => (url.pathname !== '/home'), [url.pathname])

  useEffect(() => {
  // 判断是否隐藏地图
    if (!hidden) {
      //负载较高的初始化操作
      init()   //需要判断是否已经初始化
    }
  }, [hidden])

  return (
    <div className={hidden ? styles.hide : ''} ></div>
  )
}

export default HidComponent

效果对比

优化前:

每次回到地图页面都会重新加载地图,由于地图信息依赖网络。因此网速越慢地图加载延迟越久 优化前.gif

使用Redux:

报错:Warning A non-serializable value was detected in an action

使用CSS隐藏:

每次回到地图页面地图都是已经加载好的状态 优化后 (2).gif