uni-router v1.6.0重磅更新:安全传参新姿势

59 阅读6分钟

v1.6.0 新增 params 页面参数传递、参数持久化存储、查询参数增强方法,修复导航解析阶段 params 丢失问题

前言

uni-app 的页面间数据传递一直是个痛点。query 参数只能传字符串,复杂数据需要手动 JSON.stringify/parse,且暴露在 URL 中不安全;uni.setStorageSync 虽然能存复杂数据,但需要手动管理生命周期,容易遗漏清理。

v1.6.0 引入 params 机制,让页面间传递复杂数据变得像 query 一样简单,同时不暴露在 URL 中。配合 queryInt / queryNumber / queryBool 查询参数增强方法,uni-router 的数据传递能力更加完整。


一、页面参数传递(params)

为什么需要 params?

uni-app 的 query 参数存在两个限制:

  1. 仅支持字符串router.push({ path: '/pages/detail', query: { price: 19.99 } }) 中的 price 在目标页面读取时是字符串 '19.99',需要手动转换
  2. 暴露在 URL 中:敏感数据(如用户信息、订单详情)不适合放在 query 中

params 解决了这两个问题:支持传递任意 JSON 可序列化数据(对象、数组、嵌套结构),且不暴露在 URL 中。

基本用法

发起导航 — 传入 params

await router.push({
	path: '/pages/detail/index',
	query: { id: 'order-001' }, // query 仍用于 URL 可见参数
	params: {
		// params 不暴露在 URL 中
		userInfo: { name: 'Tom', age: 20 },
		tags: ['vip', 'active'],
		orderDetail: { items: [{ name: '商品A', qty: 2 }], total: 199.9 }
	}
})

目标页面 — 读取 params

import { useRoute } from '@meng-xi/uni-router'

const route = useRoute()

// route.value.params 是只读对象
console.log(route.value.params.userInfo) // { name: 'Tom', age: 20 }
console.log(route.value.params.tags) // ['vip', 'active']

params 类型定义

// 页面参数值类型 — 支持 JSON 可序列化数据
type ParamValue = string | number | boolean | null | ParamValue[] | { [key: string]: ParamValue }

// 页面参数对象
interface ParamObject {
	[key: string]: ParamValue
}

// RouteLocation 中的 params 字段
interface RouteLocation {
	// ...
	params: Readonly<ParamObject>
}

params 与 query 的区别

特性queryparams
URL 可见
数据类型仅字符串(Record<string, string>任意 JSON 可序列化数据
刷新保留是(H5)否(默认),开启 persistent 后是
适用场景页面标识、简单筛选复杂数据传递、敏感信息

二、参数持久化存储(persistent)

为什么需要持久化?

默认情况下,params 存储在内存中。在 H5 平台上,用户刷新页面后内存数据丢失,route.params 将为空对象。persistent 选项将 params 持久化到 uni.setStorageSync,刷新后仍可读取。

单次导航持久化

await router.push({
	path: '/pages/detail/index',
	query: { id: 'persistent-demo' },
	params: { bigData: { items: [1, 2, 3], total: 3 } },
	persistent: true // 此次的 params 持久化到 storage
})

全局默认持久化

通过 paramsPersistent 配置项设置全局默认值,避免每次导航都手动指定:

const router = createRouter({
	routes,
	paramsPersistent: true // 所有 params 默认持久化
})

// 单次导航可通过 persistent 覆盖全局默认值
await router.push({
	path: '/pages/detail/index',
	params: { tempData: '不需要持久化' },
	persistent: false // 覆盖全局默认值,此次不持久化
})

持久化原理

  • persistent: true 时,params 同时写入内存 Map 和 uni.setStorageSync
  • persistent: false(默认)时,params 仅写入内存 Map
  • 读取时优先从内存获取,内存未命中再从 storage 读取
  • 路由器在 syncRoute() 时自动清理不再需要的 params(通过 cleanupStale() 惰性清理)

三、查询参数增强方法

问题背景

uni-app 的 query 参数始终是字符串类型。即使传入 router.push({ query: { price: 19.99, enabled: true } }),在目标页面读取时也是 '19.99''true',需要手动转换:

// 没有增强方法时的写法
const price = Number(route.value.query.price) || 0
const enabled = route.value.query.enabled === 'true'

queryInt / queryNumber / queryBool

v1.6.0 为 RouteLocation 新增三个便捷方法,自动完成类型转换:

const route = useRoute()

// queryInt — 解析为整数
const id = route.value.queryInt('id') // '42' → 42
const page = route.value.queryInt('page', 1) // 缺失时返回默认值 1

// queryNumber — 解析为数值(支持浮点)
const price = route.value.queryNumber('price') // '19.99' → 19.99
const total = route.value.queryNumber('total', 0) // 缺失时返回默认值 0

// queryBool — 解析为布尔值
const enabled = route.value.queryBool('enabled') // 'true' → true, '1' → true
const visible = route.value.queryBool('visible', true) // 缺失时返回默认值 true

方法签名

interface RouteLocation {
	// ...
	queryInt(key: string, defaultValue?: number): number | undefined
	queryNumber(key: string, defaultValue?: number): number | undefined
	queryBool(key: string, defaultValue?: boolean): boolean | undefined
}

解析规则

方法输入输出说明
queryInt'42'42使用 parseInt 解析
queryInt'3.14'3截断小数部分
queryInt'abc'defaultValue解析失败返回默认值
queryNumber'19.99'19.99使用 parseFloat 解析
queryNumber'abc'defaultValue解析失败返回默认值
queryBool'true' / '1'true识别为 true 的值
queryBool'false' / '0'false识别为 false 的值
queryBool'yes'defaultValue无法识别返回默认值

四、RouterLink 新增 params 与 persistent

声明式导航组件 RouterLink 同步支持 paramspersistent

<template>
	<!-- 带 params 跳转 -->
	<mxuni-router :to="{ path: '/pages/detail/index', query: { id: 'link-params' } }" :params="{ orderInfo: { orderId: 'A001', amount: 99.9 } }">
		<view class="btn">查看订单详情</view>
	</mxuni-router>

	<!-- params 持久化 -->
	<mxuni-router :to="{ path: '/pages/detail/index', query: { id: 'link-persistent' } }" :params="{ config: { theme: 'dark' } }" persistent>
		<view class="btn">查看配置(刷新后仍可读取)</view>
	</mxuni-router>
</template>

新增 Props

Prop类型默认值说明
paramsParamObjectundefined页面参数,支持复杂数据(仅 JSON 可序列化值),不暴露在 URL 中
persistentbooleanundefined页面参数是否持久化到 storage,H5 刷新后仍可读取

五、RouterOptions 新增 paramsPersistent

选项类型默认值说明
paramsPersistentbooleanfalse页面参数持久化默认值,设为 true 时所有 params 默认通过 uni.setStorageSync 持久化存储,H5 刷新后仍可读取,单次导航可通过 persistent 选项覆盖

六、RouteLocationPathRaw / RouteLocationNamedRaw 新增字段

字段类型默认值说明
paramsParamObjectundefined页面参数,支持复杂数据(仅 JSON 可序列化值),不暴露在 URL 中
persistentbooleanundefined页面参数是否持久化到 storage,H5 刷新后仍可读取

七、Bug 修复

paramsManager.get() 惰性清理导致导航时 params 丢失

问题:使用 params 跳转到目标页面时,route.params 为空对象 {}

根因paramsManager.get() 在读取 params 时会执行惰性清理——检查当前页面栈中是否还有页面在使用该 params key,如果没有则删除。但在 matcher.resolve() 阶段(导航执行前),目标页面尚未入栈,导致 params 被误删并返回 undefined

修复

  1. 新增 peek() 方法——读取 params 但不做惰性清理
  2. extractParamssyncCurrentRoute 改用 peek 读取 params
  3. syncRoute() 中通过 cleanupStale() 做显式清理,确保页面离开后 params 被正确回收
// 修复前
function extractParams(query: Record<string, string>): ParamObject | undefined {
	const key = query[PARAMS_KEY]
	if (!key) return undefined
	delete query[PARAMS_KEY]
	return paramsManager.get(decodeURIComponent(key)) // get 会做惰性清理,可能误删
}

// 修复后
function extractParams(query: Record<string, string>): ParamObject | undefined {
	const key = query[PARAMS_KEY]
	if (!key) return undefined
	delete query[PARAMS_KEY]
	return paramsManager.peek(decodeURIComponent(key)) // peek 只读取,不清理
}

八、类型导出更新

v1.6.0 新增以下类型导出:

import type { QueryValue, ParamValue, ParamObject } from '@meng-xi/uni-router'

// QueryValue — query 参数输入类型
type QueryValue = string | number | boolean

// ParamValue — params 参数值类型
type ParamValue = string | number | boolean | null | ParamValue[] | { [key: string]: ParamValue }

// ParamObject — params 参数对象类型
interface ParamObject {
	[key: string]: ParamValue
}

升级指南

v1.6.0 完全向后兼容,无需修改现有代码即可升级。以下是推荐的新功能接入方式:

1. 使用 params 替代手动序列化

// 之前:手动 JSON.stringify/parse
uni.setStorageSync('orderData', JSON.stringify(orderData))
await router.push({ path: '/pages/detail/index', query: { id: '001' } })
// 目标页面
const orderData = JSON.parse(uni.getStorageSync('orderData') || '{}')

// 现在:直接使用 params
await router.push({
	path: '/pages/detail/index',
	query: { id: '001' },
	params: { orderData }
})
// 目标页面
const orderData = route.value.params.orderData

2. 使用查询参数增强方法替代手动转换

// 之前
const id = parseInt(route.value.query.id as string) || 0
const enabled = route.value.query.enabled === 'true'

// 现在
const id = route.value.queryInt('id', 0)
const enabled = route.value.queryBool('enabled', false)

3. H5 场景开启 paramsPersistent

如果项目需要 H5 刷新后仍保留 params,可在创建路由器时开启全局默认值:

const router = createRouter({
	routes,
	paramsPersistent: true
})