React中使用Redux实现高德地图AMap地图实例的缓存和复用

简介

问题

在项目开发中,为了避免内存泄漏,通常需要在组件卸载时销毁地图实例。然而,如果用户频繁地进入和离开地图页面,每次重新加载地图都会导致不必要的性能损耗,从而影响用户体验。

解决方案

为了提高性能,可以将地图实例进行缓存,以便在需要时重新渲染地图,而不必每次都重新创建。以下是一些实现此目标的方法:

  1. 使用React Context
    • 优点:React Context是React的内置功能,无需额外安装库。它可以让你在组件树中轻松共享状态,而无需通过props逐层传递数据。
    • 缺点:Context不适合存储大量的状态,因为每次Context更新时,所有使用该Context的组件都会重新渲染。此外,Context的使用可能会使代码变得复杂,特别是在大型应用中。
  2. 使用Redux
    • 优点:Redux提供了一个集中的、可预测的状态管理解决方案。它允许你在应用的任何地方访问状态,同时还提供了强大的开发工具,如时间旅行调试。
    • 缺点:Redux的学习曲线较陡峭,需要理解一些新概念(如reducers和actions)。此外,Redux可能会引入一些模板代码,特别是对于简单的应用来说。
  3. 使用localStorage或sessionStorage
    • 优点:localStorage和sessionStorage是Web API的一部分,无需安装任何库。它们允许你在用户的浏览器中持久化存储数据,即使在页面刷新后也能保留数据。
    • 缺点:localStorage和sessionStorage的存储空间有限(通常为5MB)。此外,它们只能存储字符串,这意味着你需要将对象转换为字符串才能存储,并在读取时将其解析回对象。

由于我的项目已经使用Redux进行状态管理,因此选择使用Redux来实现地图实例的缓存和重用。

实现步骤

如果您已熟悉Redux,可以直接查看下面的实现步骤。否则,建议先访问Redux官网深入学习Redux,然后返回此处,开始从第3步了解如何在您的项目中使用Redux。

  1. 安装redux和redux toolkit
js 复制代码
// NPM
npm install redux
npm install @reduxjs/toolkit

// Yarn
yarn add redux
yarn add @reduxjs/toolkit
  1. 使用Provider包装应用 : 在主入口文件(例如main.js)中,使用Provider来包装整个React应用。这允许您的应用访问Redux存储。
js 复制代码
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { Provider } from 'react-redux'
import { store } from '@/store/store'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)
  1. 创建一个Redux Slice来管理地图实例的状态。在示例中,我们创建了mapSlice.ts文件,并定义了setMap reducer来保存地图实例。
js 复制代码
import { createSlice } from '@reduxjs/toolkit'

interface map {
  amap: any
}

// 默认值
const initialState: map = {
  amap: null  // 默认值为null
}

export const mapSlice = createSlice({
  name: 'mapInstance',
  initialState,
  reducers: {
    // 保存地图实例
    setMap: (state, action) => {
      state.amap = action.payload
    }
  }
})

export const { setMap } = mapSlice.actions

export default mapSlice.reducer
  1. 创建Redux存储(store)来集中管理状态。示例中的store.ts文件定义了Redux存储并将mapSlice添加到存储配置中。
js 复制代码
import { configureStore } from '@reduxjs/toolkit'
import mapSlice from './map/mapSlice'

export const store = configureStore({
  reducer: {
    map: mapSlice
  }
})

// 从 store 本身推断出 `RootState` 和 `AppDispatch` 类型
export type RootState = ReturnType<typeof store.getState>
// 推断出类型: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
  1. 在组件中使用Redux管理地图实例 : 在组件中使用useSelectoruseDispatch来访问Redux状态和操作。在示例中的MapContainer.tsx文件中,我们获取保存的地图实例并在需要时进行重用。
js 复制代码
import { useEffect } from 'react'
import AMapLoader from '@amap/amap-jsapi-loader'
import { useDispatch, useSelector } from 'react-redux'
import { setMap } from '@/store/map/mapSlice'

const MapContainer: React.FC = () => {
  // 获取之前保存的地图实例
  const { amap } = useSelector(state => state.map)
  // 用于调用action
  const dispatch = useDispatch()

  let map = amap // 地图实例

  useEffect(() => {
    // 存在地图实例时直接使用现有实例
    if (map !== null) {
      const containerParent = document.getElementById('containerParent')
      if (containerParent !== null) {
        // 将空容器替换成地图
        const container = document.getElementById('container')
        if (container !== null) {
          containerParent.removeChild(container)
        }
        containerParent.insertBefore(map.getContainer(), containerParent.firstChild)
      }
    } else {
      // 不存在地图实例时重新加载地图
      AMapLoader.load({
        key: 'xxxxxxx', // 高德地图Web端开发者Key
        version: '2.0',
        plugins: [] // 需要使用的的插件列表(必填项)
      })
        .then((AMap) => {
          map = new AMap.Map('container', {
            viewMode: '3D', // 3D地图模式
            zoom: 17, // 地图比例尺
            center: [120, 30] // 初始化地图中心点位置
          })
          
          ......
          ......
          
        })
        .catch((e) => {
          console.log(e)
        })
    }
    // 组件卸载时保存地图实例
    return () => {
      dispatch(setMap(map))
    }
  }, [])

  return (
    <div id='containerParent' style={{ position: 'relative', width: '100%', height: 'calc(100vh - 64px)' }}>
      <div id="container" style={{ width: '100%', height: '100%' }}></div>
    </div>
  )
}

export default MapContainer

效果对比

优化前:

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

优化后:

每次回到地图页面地图都是已经加载好的状态

相关推荐
ClareXi3 小时前
react项目通过http调用后端springboot服务最简单示例
spring boot·react.js·http
咔咔库奇15 小时前
react动态路由
前端·react.js·前端框架
yqcoder16 小时前
react 中 FC 模块作用
前端·react.js·前端框架
刘志辉17 小时前
react的创建与书写
前端·react.js·前端框架
奔跑草-19 小时前
【前端】深入浅出的React.js详解
前端·react.js·前端框架
小牛itbull20 小时前
ReactPress:深入解析技术方案设计与源码
javascript·react.js·reactpress
秃头女孩y20 小时前
【React】条件渲染——逻辑与&&运算符
前端·react.js·前端框架
小满zs21 小时前
React第十五章(useEffect)
前端·react.js
破浪前行·吴1 天前
使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
前端·react.js·three.js
咔咔库奇1 天前
react之了解jsx
前端·javascript·react.js