<template>
<div>
<button @click="navigateTo('/pages/components/swiper/index?name=usmile%e7%94%b5')">
swiper1
</button>
<button @click="navigateTo('/pages/components/swiper/index?name=usmile电')">
swiper2
</button>
{{ option }}
</div>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
const option = ref("")
onLoad((op) => {
console.log('onLoad', op)
option.value = JSON.stringify(op || {})
})
function navigateTo(url: string) {
uni.navigateTo({
url: url,
})
}
</script>
<style scoped></style>
- 发布:2026-02-27 15:09
- 更新:2026-02-28 16:03
- 阅读:169
产品分类: uniapp/H5
PC开发环境操作系统: Mac
PC开发环境操作系统版本号: 26.3
浏览器平台: Chrome
浏览器版本: 145.0.7632.111(正式版本) (arm64)
项目创建方式: CLI
CLI版本号: 3.0.0-alpha-4070620250731001
示例代码:
操作步骤:
见BUG描述和附件demo工程
uni.navigateTo({url: '/pages/components/swiper/index?name=usmile%e7%94%b5'})
见BUG描述和附件demo工程
uni.navigateTo({url: '/pages/components/swiper/index?name=usmile%e7%94%b5'})
预期结果:
跳转后
location.herf = http://localhost:5173/#/pages/components/swiper/index?name=usmile%e7%94%b5
跳转后
location.herf = http://localhost:5173/#/pages/components/swiper/index?name=usmile%e7%94%b5 实际结果:
跳转后
location.herf = http://localhost:5173/#/pages/components/swiper/index?name=usmile%25e7%2594%25b5
跳转后
location.herf = http://localhost:5173/#/pages/components/swiper/index?name=usmile%25e7%2594%25b5 bug描述:
本地调试,http://localhost:5173/
通过uni.navigateTo({url: '/pages/components/swiper/index?name=usmile%e7%94%b5'}) 打开页面后。
通过location.href获取到的页面地址为 http://localhost:5173/#/pages/components/swiper/index?name=usmile%25e7%2594%25b5
看得出来location.href拿到的name = encodeURIComponent('usmile%e7%94%b5')
附件是demo
欢迎到专业群( uni-app 官方技术交流群 1 ) 咨询,群中有相关专业的管理员和群友。
作为官方评审人员,我来对这个 bug 反馈进行评审:
一、反馈完整性评估
✅ 内容完整性较好
- BUG标题:明确指出了"H5 navigateTo打开页面后,当前 location.href 编码异常"
- BUG描述:详细说明了问题现象,包括具体的URL变化对比
- 代码示例:提供了可直接运行的最小复现demo
- 复现步骤:与代码示例结合,描述清晰
- 预期/实际结果:对比明确,用具体URL展示差异
- 环境信息:包含了平台(Mac)、工具版本(HBuilderX正式版)等关键信息
⚠️ 需要补充的信息
- HBuilderX具体版本号:只写了"正式版",建议补充如"4.87"这样的具体版本
- uni-app版本:未明确是vue2还是vue3项目
- 测试浏览器:未说明测试用的浏览器及版本
二、问题合理性分析
✅ 预期结果合理
用户期望的location.href保持usmile%e7%94%b5是合理的,因为:
%e7%94%b5已经是URL编码后的"电"字- 不应该被二次编码为
%25e7%2594%25b5
✅ 实际结果确认异常
实际出现的二次编码现象确实不符合标准URL处理规范,属于异常行为。
三、技术依据分析
根据知识库中的router文档:
- 明确提到"参数中出现空格等特殊字符时需要对参数进行编码"
- 但文档中使用的是
encodeURIComponent单次编码,没有提及二次编码问题
四、结论与建议
? 确认为有效bug
这是一个有效的bug反馈,问题表现为:
- uni-app H5端路由处理时对已URL编码的参数进行了二次编码
- 影响了通过location.href获取URL的准确性
? 处理建议
- 优先级:建议标记为中优先级,影响H5端URL参数解析
- 临时方案:用户可在目标页面使用
decodeURIComponent二次解码作为临时解决方案 - 修复方向:需要检查uni-app H5端路由跳转时对URL参数的处理逻辑,避免对已编码参数重复编码
? 补充验证建议
建议用户在目标页面添加验证代码:
onLoad((options) => {
console.log('原始参数:', options.name)
console.log('解码后:', decodeURIComponent(options.name))
console.log('二次解码:', decodeURIComponent(decodeURIComponent(options.name)))
})
这个反馈整体质量较高,信息完整,复现路径清晰,可以进入bug修复流程。
骑猪o0o找牛 - 一挖一麻袋
先说 原理吧
-
uni.navigateTo后
-
然后会调用beforeInvokeApi
在/node_modules/@dcloudio/uni-h5/dist/uni-h5.es.js -
然后调用normalizeUrl
-
然后调用encodeQueryString
也就是 这里 解析 你 传递的 url, 然后 又给你 编码了一次// 要编码... function encodeQueryString(url) { if (!isString(url)) { return url; } const index2 = url.indexOf("?"); if (index2 === -1) { return url; } // 获取query const query = url.slice(index2 + 1).trim().replace(/^(\?|#|&)/, ""); if (!query) { return url; } // 页面路由 url = url.slice(0, index2); // 遍历获取 query const params = []; query.split("&").forEach((param) => { const parts = param.replace(/\+/g, " ").split("="); const key = parts.shift(); const val = parts.length > 0 ? parts.join("=") : ""; // 然后 编码了一次.... params.push(key + "=" + encodeURIComponent(val)); }); return params.length ? url + "?" + params.join("&") : url; } -
准备调用 vue-router
function navigate({ type, url, tabBarText, events, isAutomatedTesting }, __id__) { if (process.env.NODE_ENV !== "production" && !__UNI_FEATURE_PAGES__) { const router = getApp().$router; // 这里的 url 已经是对 url上的query 编码一次的了 // parseUrl 解码 一次, 然后 丢给 vue-router // 那么到vue-router , 拿到的 就是 实际 uni.navigateTo 最初传递的 ~~~ const { path, query } = parseUrl(url); return new Promise((resolve, reject) => { const state2 = createPageState(type, __id__); router[type === "navigateTo" ? "push" : "replace"]({ path, query, // 这里的 query 就是 你最初 uni.navigateTo 传递的 url上的 query state: state2, force: true }).then((failure) => { ... ... }); }); } -
vue-router要 push 跳转了,
源码里面 会 使用 fullPath 作为 history.pushState 的参数
而这个 fullPath 是 经过 vue-router 的 url编码 , 不等同于 encodeURIComponent,
vue-router的编码是
vue-router 有个 配置项 就是 配置 这个, 叫
stringifyQueryfunction encodeQueryValue(text) { return (commonEncode(text) // Encode the space as +, encode the + to differentiate it from the space // /\+/g .replace(PLUS_RE, '%2B') // // /%20/g .replace(ENC_SPACE_RE, '+') // /#/g .replace(HASH_RE, '%23') // /&/g .replace(AMPERSAND_RE, '%26') // /%60/g .replace(ENC_BACKTICK_RE, '`') // /%7B/g .replace(ENC_CURLY_OPEN_RE, '{') // /%7D/g .replace(ENC_CURLY_CLOSE_RE, '}') // /%5E/g .replace(ENC_CARET_RE, '^')); }
完整流程
如果 你 先编码 再调用 uni.navigateTo
那就是
编码1次 -> uni.navigateTo -> uni又编码(此时 编码2次) -> 丢给 vue-router, 解码1 (此时 编码又变成1) -> vue-router 生成fullPath 给 history.pushState(编码又变成2次)
最终链接上 显示 的 编码2次的
-
再说一下 h5端 onLoad 中 参数 options 的 问题
options 是啥呢?
源码
setup(instance2) {
instance2.$pageInstance = instance2;
// 这里 就是 取的 useRoute
const route = usePageRoute();
// 先获取 query , 这里的query 就是 vue-router 时 拿到的 原文query , 跳转的时候 用的是 route.fullPath 是 路由 + 编码后的 query
// uni 这里 又 解码了一次...
const query = decodedQuery(route.query);
// 保存一下, 调用 onLoad 使用的就是 __pageQuery
instance2.attrs.__pageQuery = query;
getPage$BasePage(instance2.proxy).options = query;
instance2.proxy.options = query;
...
...
}
也就是 从 useRoute 获取 当前路由 对象,
useRoute().query 就是 你最初 uni.navigateTo传递的 url上的 query
- 你如果 编码 , 那这里就是编码的
- 如果 没有编码, 那么 这里就是 没有编码的
假设 你传递的是 /page/cc/dxd?gg=%E5%9C%9F%E8%B1%86
那么 useRoute().query 就是 { "gg": %E5%9C%9F%E8%B1%86 }
然后 被uni 解码了 ,
在 onLoad 中的 参数 就是 { "gg": "土豆" }2026-02-28 13:17