Redux Toolkit存储经纬度大数组引发React Native真机性能卡顿问题
React Native + Redux Toolkit 地图轨迹追踪真机卡顿问题
问题描述
我正在使用React Native / Redux Toolkit实现地图端的用户位置记录与追踪功能。每当用户移动距离超过2米时,就会触发Redux dispatch,将更新后的坐标推入坐标数组。用户处于移动状态时,该操作触发频率约为1fps(每秒1次),长此以往会生成体量较大的[lat, long]坐标数组,应用在物理真机上运行约5分钟后就会完全卡顿,而模拟器环境下运行无异常。
目前推测性能问题主要源于以下两点:
- Redux中大数组的处理、新坐标入栈的实现方式:认为在reducer中直接使用
.push()是符合Redux Toolkit结合Immer的使用规范的。此前曾使用展开运算符...newCoords的写法,但了解到Redux Toolkit结合Immer的场景下推荐使用当前的.push()写法? - 用于渲染大数组的MapView组件:当用户位置追踪时长从数分钟延长至数小时,数组体量过大会导致组件动态更新压力陡增。是否需要做数组分片处理?还是数组体量的影响并没有预想的大?毕竟运行5分钟后数组也仅包含数百个元素,并不算体量过大?
相关代码
recordSlice.js
import { createSlice } from "@reduxjs/toolkit" export const RouteRecord = createSlice({ name: "routeRecord", initialState: { currentRouteCoordinates: [], }, reducers: { logCoords: (state, action) => { { ...... } let lat = action.payload.coords.latitude let long = action.payload.coords.longitude let alt = action.payload.coords.alt let newCoords = [long, lat, alt] state.currentRouteCoordinates.push(newCoords) { ...... } }, }, }) export const { logCoords } = RouteRecord.actions export default RouteRecord.reducer
mainStore.js
import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit" import { combineReducers } from "redux" import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, REHYDRATE, } from "redux-persist" import AsyncStorage from "@react-native-async-storage/async-storage" import RouteReducer from "../features/recordSlice" { ...... } const rootReducer = combineReducers({ record: RouteReducer, { ...... } }) const persistConfig = { key: "root", version: 1, storage: AsyncStorage, blacklist: [ { ...... } ], } const persistedReducer = persistReducer(persistConfig, rootReducer) const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }), }) export const persistor = persistStore(store) export default store
routelines.js
import { StyleSheet } from "react-native" import React from "react" import { useSelector } from "react-redux" import { ShapeSource, LineLayer } from "@rnmapbox/maps" const RouteLines = () => { const allCoords = useSelector((state) => state.record.allCoords) return ( <ShapeSource id="source2" // tolerance={1} shape={{ type: "MultiLineString", coordinates: allCoords, }} > <LineLayer id="layer2" style={styles.lineLayer} /> </ShapeSource> ) } export default RouteLines
解决方案
- 先修复基础代码错误:你的reducer中存储坐标的字段名是
currentRouteCoordinates,但路由组件里取值用的是state.record.allCoords,字段不匹配会导致取值异常,先统一字段名。 - Redux写入部分的性能没有问题:RTK+Immer环境下直接用
.push()更新数组是官方推荐写法,内部会自动处理不可变数据,性能比展开运算符更好,这部分不是卡顿诱因。 - 第一个核心卡顿原因:
redux-persist的全量写入开销。如果你没有把坐标数组加入persist黑名单,每次坐标更新都会触发整个state树的序列化,并且全量写入AsyncStorage。真机的闪存IO、JS桥接序列化性能远差于电脑端模拟器,每秒一次全量写持续5分钟后,数组体积上涨会直接阻塞JS线程。修复方式:将存储轨迹坐标的数组字段加入persistConfig的
blacklist,实时记录过程中不需要持久化坐标数据,等用户主动结束轨迹记录后,再单独把完整坐标数组一次性写入本地存储即可。 - 第二个核心卡顿原因:Mapbox ShapeSource全量重绘开销。你每次渲染都给ShapeSource传入新创建的shape对象,哪怕只新增了一个坐标点,Mapbox也会销毁原有几何要素,全量解析、桥接传输、重绘整条轨迹线。数百个点之后,JS到原生的桥接传输、原生层绘制的开销会线性上涨,引发卡顿。
修复方式:
- 用
useMemo缓存shape对象,只有坐标数组长度变化时才生成新的shape引用,避免无意义的重绘 - 不要用全量替换shape的方式更新轨迹,调用Mapbox ShapeSource提供的增量更新API,新增坐标时只往现有数据源追加单个点,不需要重绘整条已有轨迹
- 打开ShapeSource的
tolerance配置,根据当前地图缩放等级设置合理的线简化容差,减少原生层的绘制压力
- 用
- 额外优化:给坐标更新逻辑加两层过滤,一是GPS精度阈值过滤,丢弃精度差的漂移点;二是移动距离二次校验,避免静止状态下的GPS漂移触发无意义更新。如果需要支持数小时的长距离记录,可以在记录过程中定期用道格拉斯-普克算法做轨迹抽稀,在不影响视觉效果的前提下压缩坐标点数量。
内容的提问来源于stack exchange,提问作者jjrise
相关产品推荐
相关产品推荐

