Code前端首页关于Code前端联系我们

1.Vue Router对query参数的编码逻辑是啥样的?

terry 14小时前 阅读数 13 #Vue

在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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门