iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Modern Web

Vue.js 什麼意思系列 第 24

Day 24:路由搜查隊-route.query

目前我們實現了即時搜尋顯示書名相符的書單資料,但是當我切換到其他路由之後又想再回到上一頁的搜尋結果,或是直接在當前路徑重新整理頁面時,搜尋欄位就被清空了,而書單資料因為搜尋欄位無字串值可供辨別,因此連帶失去篩選條件,一切回到最一開始的樣子。

事實上,網站中的欄位並不會記住使用者的輸入值,甚至是存進 Vuex 的資料也會在刷新頁面之後被清空,因此才需要每次在不同時機進入路由或元件時,都需要發送 API 重新取得資料再渲染到畫面上,又或者是透過暫存在用戶端的 LocalStorage 或 Cookie 以便日後取用。

以為沒有地方可以存取關鍵字,但其實近在眼前就有個可以利用的工具,就是路由!大家可以觀察最常使用的 Google 搜尋,假設我們在搜尋欄位輸入「vue.js」,搜尋結果會導向「 https://www.google.com/search?q=vue.js 」(& 後面暫且略過),接著改搜尋「vuex」,變成導向「 https://www.google.com/search?q=vuex 」,有發現什麼相同和差別之處了嗎?

$route.query物件

相同的是兩者都在 /search 路由之後出現「?q=」,差別在於後方所接的是各自輸入的搜尋關鍵字,而我們就是要利用「?」來記錄搜尋字詞,問號後方所接的正是「query」查詢字串,其組成通常是一對 key 和 value,假設一段網址最後為「?search=java&user=john」(多組 query 中間以 & 串連) ,則透過 $route.query 便能取得 query 物件的內容為 {search: 'java', user: 'john'}

知道方法之後,實際應用在我們的專案範例上:

  • 原本在 <BookList> 元件上監聽的 input 事件是將輸入值直接賦值給 inputText prop,再傳遞給子元件渲染在 input 上;由於要在監聽事件增加其他執行內容,因此可改為監聽 methods 中的函式 searchBookName

    <div class="All">
      <BookList
        navTitle="All"
        :bookList="books"
        :inputText="searchText"
        @searchBook="searchBookName($event)"
      />
      <!-- 原先寫法:@searchBook="searchText = $event.target.value" -->
    </div>
    
  • searchBookName函式除了原本的賦值工作,另外追加設置 $router.replace,讓每次即時輸入字串的同時,都會將輸入值列入 query 物件中,並且更新 key 為 search 的 value 內容,於是每當輸入字串的同時,都會重新導向連動輸入字串的 query 路徑。

    methods: {
      searchBookName(event) {
        this.searchText = event.target.value;
        this.$router.replace({
          name: this.$route.name,
          query: { search: event.target.value },
        });
      },
    },
    
  • 接著在元件內使用的導航守衛 beforeRouteEnter 進行判斷,當路徑帶有指定 key 為 search 的 query 物件時,要先將 search 對應的 value 賦值給 searchText,透過 inputText prop 傳給子元件,因此當重新整理頁面時,趕在畫面渲染好之前,已經將重整頁面之前的搜尋值再次渲染至 input 欄位中了。

    這邊需要留意的是,beforeRouteEnter 階段無法取得 this,因為其會在導航確認之前被調用,當時準備進入的元件實例的尚未完成創建,不過可以在 next callback 中透過 vm 取得實例,再利用 to 取得目標路由物件中的 query 物件。

    beforeRouteEnter(to, from, next) {
      next((vm) => {
        if (to.query.search) {
          vm.searchText = to.query.search;
        }
      });
    },
    
    data() {
      return {
        searchText: "",
      };
    },
    

設置完成後,測試看看在搜尋欄輸入「vue」,檢查一下以下三種情境,無誤的話就代表成功了!

  1. 網址變成「http://localhost:8080/book/all?search=vue
  2. 重新整理頁面後,會再顯示同樣的搜尋結果
  3. 切換至其他路由後,再按返回上一頁或前往下一頁,都能再次顯示同樣的搜尋結果
    query

$router.replace VS $router.push

簡單說明兩者的差別在於 $router.replace 不會向 history 留下紀錄。以上述範例為例,若使用 $router.push,則當我輸入「java」之後,按下返回上一頁,會使路徑變成「 http://localhost:8080/book/all?search=jav 」;而使用 $router.replace 的話,上一頁會是進入當前路由的上一個頁面。

相關實作也可以應用在登入流程,假設一個使用者的動線是從首頁進入會員登入頁,若登入成功之後應該透過 $router.replace 將他導向會員後台,此時他若想要返回上一頁,就會因為在登入頁沒有留下 history 紀錄,而跳過登入頁又再次回到一開始的進入點首頁,因為對一個登入成功的使用者來說,不適合以登入狀態能夠再次回到登入頁。

參考資料


上一篇
Day 23:兒子有事交給爸-$emit 傳出事件
下一篇
Day 25:從頭開始的 Scroll Behavior
系列文
Vue.js 什麼意思30

尚未有邦友留言

立即登入留言