What is the difference when visit the link through router-link and reload it by the same URL ? - Vue - Vue Router App

Hello, everyone,

have a nice day,

I have a project that syncs the URL with selection on the component.

I pass the query object to the VueRouter.

The VueRouter stringify that object and use it as an URL

After that, I can get that Object through this.$route.query object (Vuerouter will parse that URL to normal Object)

Here is the minimal version on CodeSandbox

enter image description here

I pass these objects to Vue router:
{ destinations: ['Hanoi'] }
{ destinations: ['Hanoi','Da Nang'] }
{ destinations: ['Ho Chi Minh City'], travelStyle:['Discovery','Adventure'] }

App.vue

<template>
  <div id="app">
    <div class="tour">
      <h2>Which tour?</h2>
      <router-link :to="{ path: 'tours', query: { destinations: ['Hanoi'] } }">Hanoi</router-link>
      <br>
      <router-link
        :to="{ path: 'tours', query: { destinations: ['Hanoi','Da Nang'] } }"
      >Hanoi - Da Nang</router-link>
      <br>
      <router-link
        :to="{ path: 'tours', query: { destinations: ['Ho Chi Minh City'] } }"
      >Ho Chi Minh City</router-link>
      <br>
      <br>
      <span>Route Query: {{this.$route.query}}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

The problem is, if I using default parseQuery/StringifyQuery of VueRouter, the URL quite of long

tours?destinations=Ho%20Chi%20Minh%20City&travelStyle=Discovery&travelStyle=Adventure

for this object: { destinations: ['Ho Chi Minh City'], travelStyle:['Discovery','Adventure'] }

I followed the instruction in this topic Custom querystring parser and add qs as a custom querystring parser.

And using an external package allow me to control the URL as what I want,

Here is inside the router file - full running project in codesandbox

// ...

const router = new VueRouter({
	mode: "history",
	base: "/",
	routes,

	stringifyQuery: (query) => {
		qs.stringify(query, {
			encode: false,
			indices: false,
			arrayFormat: "comma",
			addQueryPrefix: true,
		});
	},

	parseQuery: (query) => {
		console.log("queryString", query);
		const a = qs.parse(query, {
			comma: true,
		});
		console.log("after parse:", a);
		return a;
	},

});

Now the URL is quite shorter when using commas in array.

tours?destinations=Hanoi
tours?destinations=Hanoi,Da%20Nang
tours?destinations=Ho%20Chi%20Minh%20City&travelStyle=Discovery,Adventure

But another problem coming up: Stringify with comma format should add square brackets to parameter name to identity array with single value

Because using a comma to separate each element inside the array, so we cannot know that destinations=Hanoi means { "destinations": [ "Hanoi" ] } or { "destinations": "Hanoi" }

**When I click to routerlink to the URL tours?destinations=Hanoi I got { "destinations": [ "Hanoi" ] } (correct)
enter image description here

but if I reload that very URL I got { "destinations": "Hanoi" } (wrong)**
enter image description here

From that issues, they said we can add brackets on the end of the key on an array with a single value to get what we want.

des[]=hanoi > { des: [ 'hanoi' ] }

des=hanoi,hai phong > { des: [ 'hanoi', 'hai phong' ] }

here is my test file on repl.it


const  qs = require('qs');


const stringifyQuery = (query) => {
   Object.keys(query).forEach((item) => {

      if(Array.isArray(query[item])&&query[item].length===1) {

          query[item+'[]'] = query[item]; 
          delete query[item];
      }
  });


  return qs.stringify(query, {
      encode: false,
      indices: false,
      arrayFormat: 'comma',

  });
};

let parseQuery = (query) =>
    qs.parse(query, {
      comma: true,
    });


const a = {des: ['hanoi']};

const b = {des: ['hanoi','hai phong']};


console.log(stringifyQuery(a)); // des[]=hanoi
console.log(stringifyQuery(b)); // des=hanoi,hai phong
console.log(parseQuery(stringifyQuery(a))); // { des: [ 'hanoi' ] }
console.log(parseQuery(stringifyQuery(b))); // { des: [ 'hanoi', 'hai phong' ] }

It’s working fine!

But when I use it on vue-router, it not.

Once again, it’s showing different results between click to the router-link and reload the same URL!

For this URL tours?destinations[]=Hanoi

If I click through router-link, it still got { "destinations[]": [ "Hanoi" ] } instead "destinations": [ "Hanoi" ]

enter image description here

But if I reload the page, I got the correct one: { "destinations": [ "Hanoi" ] }

enter image description here

I tried to figure out what happened, but it’s quite touching for me as a newbie,

Does anyone have any idea or keyword in this problem?

Thanks a lot when you take your valuable time and read my long question,

It means a lot to me, I really appreciate it!

Hi, here is no standardized way to format arrays of values in query parameters.

Arrays in query parameters are sometimes represented by repeating a parameter multiple times:

?a=bar&a=foo

Sometimes by repeating a parameter along with empty square brackets:

?a[]=foo&a[]=bar

Sometimes by simply providing a list of comma-separated values:

?a=foo, bar

If you want to use the second way, set arrayFormat to brackets.

	return qs.stringify(query, {
			encode: false,
			indices: false,
			arrayFormat: "brackets",
			addQueryPrefix: true,
		});
	},
1 Like

Thank @regchiu for your reply!

Actually, I want to use a comma, so I can get the shorter query at all,

?a[]=foo&a[]=bar will become ?a=foo,bar

It makes the URL more clean and clear when that array becomes bigger.

And then, that’s problem started, we cannot know ?a=foo mean a: 'foo' or a: ['foo']

When I try to modify the custom queryString parser to add brackets as ?a[]=foo and it works in theory

But when I try to implement it on Vuerouter, it just work when I visit the page by URL ( meaning I press F5 to reload a whole page - or I copy and paste the URL to complete new tab )

It not work if I click to the router-link point to that,

I don’t know about what mechanism of vue router - what is the difference when visiting the link through routerlink and reload the whole page

Maybe some thing block Vuerouter run my custom parse :cold_sweat::cold_sweat:

Once again, thank for your reading my whole messy topic and give me a reply, that means a lot for me

I got the answer,

In my custom StringifyQuery, I mutate the query Object to add square brackets to it key (destinations[]: Hanoi)

And then after that, the router still use that object and show in the next route

That why, when I console.log this.$route.query it shows the edited query (destinations[]: Hanoi)

To fix that, I have to avoid mutating the query object, and that’s all

image

My lesson: Never mess with an outside object, we have to clone it (deep or shadow base on context) and edit it in the clone version

1 Like