iT邦幫忙

2021 iThome 鐵人賽

DAY 13
2
Modern Web

不只懂 Vue 語法:Vue.js 觀念篇系列 第 13

不只懂 Vue 語法:如何透過路由實現跨頁面傳遞資料?

  • 分享至 

  • xImage
  •  

問題回答

在跳轉頁面時,可以透過路由物件裏 params 或 query 來傳遞資料,也可以使用各種不同模式的 Route props 來傳遞資料。前者需要依賴 URL ,例如 params 需要依靠動態路由,即是/example/:id 來完成。query 則需要在 URL 裏寫 /example?id=123 這樣的格式來傳送。

相反,各種模式的 route props 就比較靈活,不用依賴 URL,而且不用透過 route 物件來取得資料像是 this.$route.params 這樣的寫法就能省下。因為我們可以直接在頁面元件設定 props,直接用意 props 來取得經由路由傳遞過來的資料。

以下會再作詳細解說。

前言

但在進入主題之前,先簡單重溫在 Vue 有什麼方法實現跨頁面資料:

  • eventbus(Vue 2) / mitt(Vue 3)
  • route props (params, query 等等)
  • Vuex

注意,目前討論的情況是跨頁面傳資料,不是父子元件之間傳資料,因此 props/emit 以及 provide/inject 方法就不適用於此情況。

回到重點,此文章會集中說明 route props 的方法。

常用的 params 和 query

先說明最簡單的 params 和 query 用法。這也是新手剛學 Vue 時最常用到的方法。

先說明 params,做法是在路由設定參數:

const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})

當輸入 /user/111 後,可以在 route object 裏取得 params:

console.log(this.$route.params.id) 
// 111

第二種是 query,路由直接設定為 /user 即可。但 URL 必須使用這格式:/user?id=111

同樣地,使用 route object 取得由以上路由傳來的 query,結果會回傳一個物件:

console.log(this.$route.query) 
// {id: '111'}

使用 $router.push時,只能擇一填寫 paramspath 屬性

另外提醒,當使用 $router.push() 的方法來跳轉頁面時,要注意:

正確寫法:

const id = '123'
this.$router.push({ 
    name: 'user', // 要事先在 router 那邊命名你的元件
    params: { id } 
})

或者

this.$router.push({ path: `/user/${id}` })

錯誤寫法:

this.$router.push({ 
    path: '/user', 
    params: { id } 
})

然而,傳 query 的話就沒有此限制:

this.$router.push({
    path: '/user',
    query: { id }
})

params 與 query 的分別

1. params 可以不顯示在 URL 裏

query 所傳送的參數會顯示在 URL 裏,但 params 則不一定。像以下寫法:

const id = "111";
this.$router.push({
    name: 'User', // 即是 /user 
    params: { id }
});

結果會跳轉到 /user 此 URL,在 URL 裏不會顯示 id 的值。反之,使用 query 的話,參數一定會顯示在 URL 裏。

2. 重刷頁面後會失去 params 資料,但 query 資料仍存在

很重要一點,重刷頁面時,如果使用 query 傳參,資料仍然會存在。反之,params 傳參的話,資料就會消失。

如何傳物件資料,或多筆資料?

說到重點了,如果我要跨頁面傳多筆資料,或者是物件資料。如何使用路由傳參的方法來完成?

傳送多個值

如果要傳送多個值,使用 paramsquery 也可。

前者的話,只要在$router.push()裏的 params 物件裏再塞資料就可以:

this.$router.push({
    name: 'User',
    params: { 
        id: 111, // 注意,傳送後會轉為字串
        name: 'Alysa'
    }
});

query 的做法:

this.$router.push("/user?id=111&name=Alysa")

this.$router.push({
    path: '/user',
    query: { 
        id: 111, // 注意,傳送後會轉為字串 
        name: 'Alysa'
    }
})

傳送資料型別會變成字串

然而,以上示範可見,params 和 query 所傳送的值都會變為字串型別。因此如果值是物件的話,就沒法傳送。不然會變成 "[object Object]"。這情況下,如果透過 params 來傳資料就會有問題。解決方法會用 JSON.parseJSON.stringify 來轉換資料。

不需要依靠 route 來傳遞資料

以上示範,我們要用 $route.params 這些方法取得 params 和 query。但如果使用 route props 的各種傳送 route props 的模式,就可以更靈活,不再使用 $router 來取得資料也行!

Boolean mode

布林模式,需要設定動態路由以及 props: true

以下例子,假設我要把 id 資料,由某一頁傳到 A 元件頁面裏使用。

router/index.js

const routes = [
  {
    path: "/",
    component: Home
  },
  {
    path: "/a/:id",
    name: "a",
    component: A,
    props: true
  }
];

A 元件

<template>
  <h1>這是 A 頁面</h1>
  <p>以下是從首頁傳來的資料:</p>
  <p>{{ id }}</p>
</template>
<script>
export default {
  props: {
    id: {
      type: String,
    },
  },
};
</script>

不用再透過 this.$route.params 來取資料了!直接在元件裏的 props 接收 id 資料,並顯示出來。但注意,元件所接收的 props,只能是 params。

Function mode

在路由物件裏,建立一個函式來回傳 props,給頁面元件使用。
跟 Boolean mode 明顯不同:

  • 不用寫動態路由
  • 可以傳 params 或 query
  • 更方便傳送物件或陣列資料

以下先示範傳 params 做法。假設我要做以下的事:

  • 在首頁打 API,取得 random user API 回傳的資料
  • 由首頁跳轉到名為 Bparams 的頁面元件
  • Bparams 頁面元件要接收到在首頁打 API 取得的資料

router/index.js

{
    path: "/b-params",
    name: "Bparams",
    component: Bparams,
    props: (route) => route.params
}

Home.vue(首頁)
按按鈕後,就跳轉到 Bparams頁面,並把 API 回傳資料用 params 傳出去。

<template>
    <h2>Function mode(傳 params)</h2>
    <p>按按鈕後打 API,用 function mode,把 API 資料傳到 B-params 頁面</p>
    <a href="#" @click.prevent="passDataToB">去 B-params 頁面</a>
</template>
export default {
  methods: {
    passDataToB() {
      fetch("https://randomuser.me/api/")
        .then((res) => res.json())
        .then((res) => {
          this.$router.push({
            name: "Bparams",
            params: {
              ...res.results[0],
            },
          });
        })
        .catch((err) => console.log(err));
    },
  },
};

Bparams.vue

<template>
  <h1>這是 B 頁面</h1>
  <p>以下是從首頁傳來的資料:</p>
  <p>{{ user }}</p>
</template>
export default {
  props: {
    user: {
      type: Object,
    },
  },
};

Object mode

物件模式適用於傳入靜態資料。直接在 props 物件裏定義要傳送的資料即可。
以下示範把資料傳入 C 頁面元件:

router/index.js

{
    path: "/c",
    name: "c",
    component: C,
    // Object mode
    props: {
      userStatic: {
        username: "Tom",
        age: 20
      }
    }
}

C.vue

<template>
    <h1>這是 C 頁面</h1>
    <p>以下是從 router props 傳來的靜態資料:</p>
    <p>{{ userStatic }}</p>
</template>
export default {
  props: {
    userStatic: {
      type: Object,
    },
  },
};

示範所有模式的程式碼

https://codesandbox.io/s/router-props-function-mode-ktzbr?file=/src/views/Home.vue

總結

  • 透過路由實現跨頁面傳遞資料,可分為兩種方法,一是依賴 URL 來使用 params 或 query。二是使用各種模式的 route props 來傳資料。
  • 當使用 this.$router.push() 來跳轉頁面時,如果是傳 params,要注意 path 和 params 屬性不能共存,只能擇一填寫。
  • 傳 params 時,值會變為字串型別。
  • 使用 route props 會比一般依賴使用 URL 來傳 params 和 query 更靈活。

參考資料

How to pass Vue Router params as props to components
【Vue.js】Vue Router 之透過路由組件傳參數給元件


上一篇
不只懂 Vue 語法:如何用 event bus 或 mitt 實現跨元件傳遞資料?
下一篇
不只懂 Vue 語法:為什麼要用 Vuex? Vuex 基本架構是怎樣?
系列文
不只懂 Vue 語法:Vue.js 觀念篇31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言