页面跳转传参最佳实践:大对象别再塞 Query 了

摘要:路由一跳,参数一长,味道就不对了。我见过最离谱的一次,是把一个几千项的商品数组直接塞进 query,地址栏跟火车一样长。刷新一下丢了,复制链接发给别人还带着半截

路由一跳,参数一长,味道就不对了。

我见过最离谱的一次,是把一个几千项的商品数组直接塞进 query,地址栏跟火车一样长。刷新一下丢了,复制链接发给别人还带着半截 %5B%7B%22id%22。这类问题,表面看是"页面跳转怎么传参",真到线上,实际是在问:大对象到底该放哪,谁来兜底,刷新怎么办。


先说一个我不太建议的姿势

很多人第一反应是:

router.push({
  path: '/confirm',
  query: {
    data: JSON.stringify(bigList)
  }
})

这玩意小数据能跑,大一点就开始冒汗。

第一,URL 有长度限制,不同浏览器、不同代理层都不一样,你不能拿它赌。第二,JSON.stringify 后字符全挤在地址栏里,既不好看也不安全。第三,用户一刷新、转发、回退,排查起来很烦,因为你根本不知道是编码丢了,还是参数被截了。

这种方案我一般只拿来传几个筛选条件,不拿来扛大数组。


真正稍微稳一点的做法,通常是三种

方式一:路由只传一个 key,数据落在 sessionStorage

这招我平时用得很多,尤其是"列表页选中一批数据,跳确认页"这种场景。因为这类数据本来就是一次性会话数据,浏览器标签页关了就算,放 sessionStorage 挺顺。

function jumpWithSessionData(path, payload) {
  const cacheKey = `jump:${Date.now()}:${Math.random().toString(16).slice(2)}`
  sessionStorage.setItem(cacheKey, JSON.stringify(payload))

  router.push({
    path,
    query: {
      cacheKey
    }
  })
}

目标页拿数据:

function readJumpData() {
  const { cacheKey } = router.currentRoute.value.query
  if (!cacheKey) return null

  const raw = sessionStorage.getItem(cacheKey)
  if (!raw) return null

  try {
    const data = JSON.parse(raw)
    sessionStorage.removeItem(cacheKey) // 用完就删,别堆垃圾
    return data
  } catch (e) {
    console.error('跳转数据解析失败', e)
    return null
  }
}

这个方案的好处很直接:URL 干净,数据量比 query 能扛得多,代码也不绕。

但它不是没坑。刷新页面还能拿到,因为 sessionStorage 还在;你把链接复制给别人,对方打不开原数据,因为对方浏览器里没有那份缓存。这一点要提前想明白。也就是说,它适合"当前用户自己跳过去继续操作",不适合"分享链接即恢复现场"。

方式二:数据放状态管理(store)

如果这份数据本来就是多个页面都会用,或者用户在当前流程里反复来回切,我更倾向直接放状态管理,不走"传大对象"这条路。

// store/orderDraft.js
import { defineStore } from 'pinia'

export const useOrderDraftStore = defineStore('orderDraft', {
  state: () => ({
    selectedRows: [],
    extraForm: {}
  }),
  actions: {
    setDraft(rows, form) {
      this.selectedRows = rows
      this.extraForm = form
    },
    clearDraft() {
      this.selectedRows = []
      this.extraForm = {}
    }
  }
})

跳转前:

const draftStore = useOrderDraftStore()

function goConfirmPage(rows, form) {
  draftStore.setDraft(rows, form)
  router.push('/confirm')
}

确认页直接取:

const draftStore = useOrderDraftStore()

onMounted(() => {
  if (!draftStore.selectedRows.length) {
    console.warn('确认页缺少草稿数据')
    router.replace('/list')
  }
})

这个方式代码最干净,尤其适合 Vue/React 这种单页应用。问题也明显:页面一刷新,内存里的状态可能没了。这个时候别硬扛,要么配持久化插件,要么就老老实实落 sessionStorage。

方式三:服务端临时落库,前端只传 token 或 id

这个办法最稳,也最适合大对象真的很大、还要支持分享、刷新恢复、多端继续的时候。比如导出任务、复杂表单草稿、批量对账结果,这些数据本来就不该全塞浏览器。

async function createJumpTicket(payload) {
  const res = await fetch('/api/temp-data/create', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  })

  const { ticket } = await res.json()

  router.push({
    path: '/result',
    query: { ticket }
  })
}

目标页再按 ticket 拉取:

async function loadByTicket(ticket) {
  const res = await fetch(`/api/temp-data/detail?ticket=${ticket}`)
  if (!res.ok) throw new Error('临时数据读取失败')
  return await res.json()
}

这招的好处是,浏览器轻松,链接还能分享,刷新也不怕。代价就是你后端得配合,还得处理过期时间、清理策略、权限校验。这地方别偷懒,尤其 ticket 如果能猜出来,等于把别人的数据挂门口了。


场景决定方案

所以这事别问"哪种最优雅",要先问场景。

  • 当前页跳下一页,数据只给当前用户临时用一次:sessionStorage + key,够了。

  • 数据是流程态,多个页面都会改,还要频繁回退前进:store 顶上。

  • 数据很大,还要求刷新不丢、链接可打开、甚至能跨端继续:直接上服务端临时存储,前端只带 id。


一个顺手的小封装

最后给一个我自己更顺手的封装,现场里拿来就能用:

export function usePageJumpCache(prefix = 'pagecache') {
  function save(data) {
    const key = `${prefix}:${Date.now()}:${Math.random().toString(36).slice(2)}`
    sessionStorage.setItem(key, JSON.stringify(data))
    return key
  }

  function read(key, remove = true) {
    const raw = sessionStorage.getItem(key)
    if (!raw) return null

    try {
      const data = JSON.parse(raw)
      if (remove) sessionStorage.removeItem(key)
      return data
    } catch (e) {
      console.error('缓存解析失败', key, e)
      return null
    }
  }

  return { save, read }
}

跳转问题,很多时候不是"怎么把 Object 带过去",而是别把不该放在 URL 里的东西,硬往 URL 里塞。

你真把这个判断顺序理清了,后面代码反而没什么玄学:小参数走 query,会话态走 sessionStorage,流程态走 store,可恢复的大数据走服务端。别一上来就 JSON.stringify 全塞地址栏,那玩意我第一眼就不太信。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_13623