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

效果对比

优化前:

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

优化后:

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

相关推荐
真的很上进37 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
wakangda12 小时前
React Native 集成原生Android功能
javascript·react native·react.js
秃头女孩y18 小时前
【React中最优雅的异步请求】
javascript·vue.js·react.js
前端小小王1 天前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 天前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
不是鱼1 天前
构建React基础及理解与Vue的区别
前端·vue.js·react.js
飞翔的渴望1 天前
antd3升级antd5总结
前端·react.js·ant design
爱喝奶茶的企鹅1 天前
Next.js 14 路由进阶:从约定式到动态路由的最佳实践
react.js
╰つ゛木槿1 天前
深入了解 React:从入门到高级应用
前端·react.js·前端框架
用户30587584891252 天前
Connected-react-router核心思路实现
react.js