今天发现了一个线上问题,我们的APP跳转内嵌H5时会默认
query带一些参数,比如city之类的,但是有时运营人员配置页面链接地址时在不太了解的情况下,会手动加上city这些参数,恰巧你请求接口的时候需要这个参数,使用this.$route.query.city获取,form提交就会成为下图,会报400参数不规范,

这样重复query传参数显然是不规范的,这样使用this.$route.query.city获取回来是一个数组
解决该问题需要两点:
- 根源上配置的时候不要重复配置
- 前端对URL
query传参进行过滤
那么过滤参数怎样才能一劳永逸呢,避免每次取参数时判断是不是数组这中重复的代码。我们使用nuxt 这中服务端渲染的框架,开始想当然的想到在middleware中过滤,每次请求过来后执行middleware,过滤参数
先写个filterQuery的测试middleware 测试覆盖原来city,跑起来看看效果吧

控制台打印结果没啥问题

在页面里取一下试试,但结果不尽人意,this.$route.query.city 依然是数组多个值


看到这里,才发现自己太想当然了,nuxt是一套vue ssr的架构,服务端渲染的项目,**每当一个新路由过来时,都会创建一个新的vue 实例,包括router, store,**这样是为了避免交叉请求状态的污染,页面中vue生命周期里this.$route是vue实例上的;改动middleware的route只在服务端比如asyncData或fetch时获取生效
测试一下
页面中asyncData获取一下参数

果然这里变了

那现在就是对router的query做处理,其实vue-router为我们提供这样的函数 parseQuery/stringQuery(提供自定义查询字符串的解析/反解析函数。覆盖默认行为)
nuxt是对vue进行了封装,路由生成都是自动通过扫页面路径自动生成的,那我们去nuxt的源码里探个究竟,生成的核心是一个模板文件,lib/app/router.js文件,创建新router实例的工厂函数也在这个文件里
export function createRouter () {
return new Router({
mode: '<%= router.mode %>',
base: '<%= router.base %>',
linkActiveClass: '<%= router.linkActiveClass %>',
linkExactActiveClass: '<%= router.linkExactActiveClass %>',
scrollBehavior,
routes: [
<%= _routes %>
],
<% if (router.parseQuery) { %>parseQuery: <%= serialize(router.parseQuery).replace('parseQuery(', 'function(') %>,<% } %>
<% if (router.stringifyQuery) { %>stringifyQuery: <%= serialize(router.stringifyQuery).replace('stringifyQuery(', 'function(') %>,<% } %>
fallback: <%= router.fallback %>
})
}
在这里找到了需要的parseQuery 函数,这里的router.parseQuery是在 lib/common/options.js中配置的
router: {
mode: 'history',
base: '/',
routes: [],
middleware: [],
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
extendRoutes: null,
scrollBehavior: null,
parseQuery: false,
stringifyQuery: false,
fallback: false
},
无论本地启动还是nuxt build的时候这个options.js都会与根目录下的nuxt.config.js配置项进行合并,所以也就一目了然了,加到nuxt.config.js的router下就好了,明确了就开干试试吧
nuxt.config.js加了如下代码
parseQuery: function (querystring) {
let d = function (str) {
try {
return str && window.decodeURIComponent(str)
} catch (e) {
return str
}
}
if (d(querystring).includes('?')) querystring = d(querystring).replace(/\?/g, '&')
querystring = querystring.split('&')
let params = {}
let pair
// march and parse
for (let i = querystring.length - 1; i >= 0; i--) {
pair = querystring[i].split('=')
params[d(pair[0])] = d(pair[1])
}
return params
}
不仅对重复的key过滤,也对url混入多个?做了合并过滤
结果就是 this.$router.query.city 进行了过滤,这里默认取第一个值

~本文完,有任何表述不清或者表达错误的,请大家指正~