1.Vue Router对query参数的编码逻辑是啥样的?
在Vue项目里用路由传参时,有没有遇到过query参数拿到手是一堆%开头的编码字符?比如想传个中文关键词,结果url里变成%E4%B8%AD%E6%96%87,取参数的时候也得“翻译”回来才行,今天就聊聊Vue Router里query参数解码的那些事儿,把原理、场景、方法都掰碎了讲清楚~
首先得明白,浏览器url里的参数可不是想怎么写就怎么写的——特殊字符(像空格、中文、&、=这些)直接放url里会乱套,所以得“编码”成安全的字符格式,Vue Router里的query参数,在跳转时会自动用encodeURIComponent
处理,把特殊字符转成百分号编码(比如中文“测试”会变成%E6%B5%8B%E8%AF%95)。
那反过来,当我们用this.$route.query.xxx
拿参数时,Vue Router内部其实已经做了一次解码!比如url里是?name=%E4%B8%AD%E6%96%87,直接打印this.$route.query.name
,拿到的会是“中文”,这时候好像不用自己解码?但别急,有两种情况得自己动手:
- 后端返回的url参数是“二次编码”的:比如后端接口把参数先encode了一次,Vue Router解码后还是编码状态(比如变成%25E4%B8%AD%E6%96%87),这时候就得自己再decode。
- 前端手动编码了参数:比如跳转时主动用
encodeURIComponent
处理了参数,那接收时自然要对应解码。
哪些场景必须手动解码query参数?
光知道原理不够,得结合实际项目场景看,这些情况碰到了,就得自己写解码逻辑:
参数包含复杂结构(比如JSON字符串)
举个例子,想把一个对象通过query传过去,前端先转成JSON字符串再encode:
// 跳转前编码 const params = { keyword: '测试&123', page: 1 }; const queryStr = encodeURIComponent(JSON.stringify(params)); this.$router.push({ path: '/search', query: { data: queryStr } });
跳转后url里的data参数是%257B%2522keyword%2522%253A%2522%25E6%25B5%258B%25E8%25AF%2595%2526123%2522%252C%2522page%2522%253A1%257D
(因为JSON.stringify后又被encode了一次),这时候用this.$route.query.data
拿到的是%7B%22keyword%22%3A%22%E6%B5%8B%E8%AF%95%26123%22%2C%22page%22%3A1%7D
,还得再decode+JSON.parse:
const decodedData = JSON.parse(decodeURIComponent(this.$route.query.data));
后端接口要求特殊编码规则
有些后端服务接收参数时,要求前端传“半编码”状态(比如只转义部分字符),这时候前端跳转前手动编码,接收时就得手动解码,或者后端返回的重定向url里,参数是经过他们自己的编码逻辑处理的,前端拿到后需要逆向解码。
多语言/特殊符号参数
比如做国际化时,locale参数可能是zh-CN&en
这种带&的(虽然实际项目里不会这么设计,但举例子),跳转时Vue Router会自动把&转成%26,这时候如果直接拿query,得到的是zh-CN%26en
,就得手动decode成zh-CN&en
。
手动解码query参数的正确姿势是啥?
核心工具是JavaScript的decodeURIComponent
方法(注意和decodeURI
的区别:前者针对查询参数这种“部分编码”,后者针对整个url的编码,项目里处理query优先用前者),具体怎么用?分步骤讲:
组件内计算属性处理
在Vue组件里,用computed把query参数批量解码,这样模板里直接用解码后的数据:
export default { name: 'SearchPage', computed: { decodedQuery() { const rawQuery = this.$route.query; // 遍历query对象,逐个解码 return Object.keys(rawQuery).reduce((acc, key) => { acc[key] = decodeURIComponent(rawQuery[key]); return acc; }, {}); } } }
模板里用{{ decodedQuery.keyword }}
,就能拿到解码后的内容。
路由守卫提前处理
如果想在组件渲染前就把参数解码好,可以用beforeRouteUpdate
(路由更新时)或beforeRouteEnter
(进入路由时)钩子:
export default { data() { return { decodedQuery: {} } }, beforeRouteUpdate(to, from, next) { // 处理to里的query参数 const newQuery = {}; for (const key in to.query) { newQuery[key] = decodeURIComponent(to.query[key]); } this.decodedQuery = newQuery; next(); // 必须调用next放行 }, beforeRouteEnter(to, from, next) { const initQuery = {}; for (const key in to.query) { initQuery[key] = decodeURIComponent(to.query[key]); } next(vm => { vm.decodedQuery = initQuery; // 把解码后的数据传给实例 }); } }
这种方式适合参数多、需要提前处理的场景,避免组件内多次解码。
封装工具函数复用
项目里多个页面要处理query解码?把逻辑封装成工具函数更高效:
// utils/route.js export function decodeQuery(query) { if (!query) return {}; const result = {}; for (const key in query) { result[key] = decodeURIComponent(query[key]); } return result; } // 组件内使用 import { decodeQuery } from '@/utils/route'; export default { computed: { decodedQuery() { return decodeQuery(this.$route.query); } } }
解码时要避开哪些“坑”?
手动解码不是无脑调用decodeURIComponent就行,这些细节不注意容易出bug:
坑一:重复解码导致乱码
前面说过,Vue Router本身会解码一次query参数,如果参数已经被Vue Router解码过,你再调用decodeURIComponent,就会把正常字符转成乱码,比如参数是“中文”,Vue Router解码后是“中文”,你再decode就会变成%E4%B8%AD%E6%96%87
(因为“中文”的UTF-8编码就是%E4...)。
怎么避免? 要明确参数的编码次数:如果是前端手动encode了一次,那解码一次;如果是后端二次编码,就解码两次?不对,得看实际场景,举个例子:
- 前端跳转时:
query: { name: encodeURIComponent('中文') }
→ url里name是%E4%B8%AD%E6%96%87 → Vue Router解码后name是“中文”(因为内部用了decodeURIComponent)→ 这时候不需要手动解码。 - 前端跳转时:
query: { name: encodeURIComponent(encodeURIComponent('中文')) }
→ url里name是%25E4%B8%AD%25E6%96%87 → Vue Router解码后name是%E4%B8%AD%E6%96%87 → 这时候需要手动decode一次,得到“中文”。
所以关键是理清参数被编码了几次,再决定解码次数。
坑二:参数值为undefined或null
如果query里某个参数的值是undefined(比如跳转时没传这个参数),调用decodeURIComponent会报错(因为参数必须是字符串),所以解码前要判断参数是否存在、是否是字符串:
export function decodeQuery(query) { if (!query) return {}; const result = {}; for (const key in query) { const value = query[key]; // 只处理字符串类型的参数,避免undefined/null报错 if (typeof value === 'string') { result[key] = decodeURIComponent(value); } else { result[key] = value; // 非字符串类型原样保留 } } return result; }
坑三:参数包含“%”本身
如果用户输入的内容里有“%”(比如密码里的特殊字符),编码后会变成%25,这时候解码逻辑是对的:%25 → decode后是%,但如果参数里本来就有%,又被错误编码多次,就会出问题,所以要保证前端跳转时的编码逻辑和后端的解码逻辑一致,避免中间环节乱编码。
Vue2和Vue3的Vue Router在解码上有区别吗?
Vue2用的是Vue Router 3.x,Vue3用的是Vue Router 4.x,核心的编码解码逻辑没变化,都是基于浏览器的URL处理规则,用encodeURIComponent和decodeURIComponent,但有个细节要注意:
- Vue Router 3.x中,query对象里的参数如果是对象或数组,会被自动转成JSON字符串并编码;
- Vue Router 4.x(Vue3)中,这种自动处理更严格,且推荐用defineAsyncComponent等新特性,但query的编码解码逻辑和浏览器的URL处理保持一致,所以手动解码的方法在两个版本中通用。
实战案例:搜索页的query解码流程
光讲理论不够,看个真实项目里的例子:
需求:做一个商品搜索页,关键词支持中文、特殊符号(amp;、#),还要传分页参数。
步骤1:跳转时的编码(前端主动控制)
// SearchInput.vue(搜索输入组件) methods: { handleSearch() { const keyword = this.keyword; // 假设用户输入“手机&电脑#平板” const page = 1; // 先把参数转成对象,再转成JSON字符串(如果需要传复杂结构),这里简化为直接传keyword // 注意:如果keyword里有特殊字符,Vue Router会自动encode,所以这里可以不用手动encode? // 测试:如果直接传 { query: { keyword, page } },url里keyword会变成“手机%26电脑%23平板” // 所以跳转代码: this.$router.push({ path: '/search', query: { keyword, page } }); } }
步骤2:接收时的解码(处理特殊字符)
// SearchPage.vue(搜索结果页) computed: { decodedKeyword() { // 因为Vue Router已经解码过一次,所以this.$route.query.keyword拿到的是“手机&电脑#平板”? // 测试:url里是?keyword=手机%26电脑%23平板 → this.$route.query.keyword → “手机&电脑#平板” // 所以其实不需要手动解码?那什么时候需要? // 假设后端要求前端传的是encode后的参数,比如后端接收的是“手机%26电脑%23平板”,这时候前端跳转时要手动encode: // 跳转时:query: { keyword: encodeURIComponent(keyword) } → url里keyword是%E6%89%8B%E6%9C%BA%2526%E7%94%B5%E8%84%91%2523%E5%B9%B3%E6%9D%BF // 这时候Vue Router解码后是“手机%26电脑%23平板” → 所以需要手动decode一次: return decodeURIComponent(this.$route.query.keyword); }, currentPage() { return Number(this.$route.query.page) || 1; } }
这个案例能看出来:是否需要手动解码,取决于参数在跳转前被编码了几次,如果是Vue Router自动编码(用户输入特殊字符,跳转时没手动处理),那Vue Router会自动解码,不需要手动处理;如果是前端手动编码(比如为了和后端规则匹配),那接收时必须手动解码。
掌握这几点,query解码不踩坑
- 理解Vue Router的自动编码/解码逻辑:基于encodeURIComponent/decodeURIComponent,大部分场景下参数会被自动解码。
- 明确手动解码的场景:后端特殊编码、前端主动二次编码、参数包含复杂结构(如JSON字符串)。
- 用对工具和方法:优先用decodeURIComponent,封装工具函数提高复用性,处理参数前做类型判断避免报错。
- 避开重复解码、空值处理、特殊字符(如%)这些坑,保证参数处理前后的一致性。
其实query解码的核心是“编码次数和解码次数一致”,就像密码锁——锁了几次,就得开几次,把这个逻辑理清楚,再结合项目实际场景,不管是Vue2还是Vue3,处理query参数都能游刃有余~
(如果想更深入,还可以研究Vue Router的源码中关于query解析的部分,看看它是怎么调用decodeURIComponent的,不过日常开发掌握上面的方法就够啦~)
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。