Validate Dynamic Route: return promise in action, auto fallback page 404 in asyncData hook

Nuxt
Promise
Error
asyncData
Vuex

需求:將錯誤的文章路徑導向error page

本站的文章結構設計為/articles/_articleId


當輸入不存在的_articleId時,希望頁面能自動導向error page


Nuxt在處理error page時,主要有兩種方式,一是在pages folder內新增_.vue檔


不屬於page路徑結構的路徑都會被導向_.vue頁面


然而這沒法處理不存在的_articleId


第二種是在layouts folder新增error.vue,說明請看官網文件


注意一點就是,雖然放在layouts folder內,但error.vue本質上是個page component!


hamsterism的error page程式碼如下:

<template>
 <app-wrapper>
  <div class="flex flex-col justify-center h-screen-70">
   <div class="h1 mb-8">
    <span class="oops">Oops!&nbsp;&nbsp;</span>Something's gone wrong..
   </div>
   <h2 class="error">
    ERROR CODE:&nbsp;&nbsp;<span class="error-code">{{
     error.statusCode
    }}</span>
   </h2>
  </div>
 </app-wrapper>
</template>
<script>
export default {
 layout: 'error',
 props: {
  error: {
   type: Object,
   default: () => {},
  },
 },
}
</script>
<style lang="postcss">
.h-screen-70 {
 height: 70vh;
}
.h1 {
 font-size: 3rem;
 overflow-wrap: break-word;
 .oops {
  font-size: 4rem;
  color: var(--btn-color-primary-dark);
 }
}
.error {
 font-size: 2rem;
 &-code {
  font-size: 4rem;
 }
}
</style>


error.vue內的props error必帶,Nuxt context的error function會自動將error object丟進這個property


所以我們的目標就變成:想辦法讓Nuxt吐出對應的error({ statusCode: "XXX", message: "OOXXOX" })


Get Article Data from Firebase

由於我直接使用nuxt/firebase module,在向firebase發請求時,我壓根沒用到axios...


先看載入文章的action code吧!

async loadCurrentArticle({ commit }, articleId) {
  // console.log('===loadCurrentArticle===')
  let article = {}
  let articleRef = await this.$fire.database.ref('articles/' + articleId)
  await articleRef.once('value', async (snapshot) => {
    article = snapshot.val()
    await commit('setCurrentArticle', article)
  })
  return new Promise((resolve, reject) => {
    if (!article) {
      reject()
    }
    resolve()
  })
},


上述寫法中,由於不管articleId存不存在,我都不會收到firebase吐給我error,它只會吐給我null的article


API請求沒發生錯誤要怎吐錯誤...卡了我一下午


因為大家都用axios處理啊 =口=


解法如上面的code,在action最後return一個Promise物件


在Promise內,透過判定article是否存在,來執行reject() or resolve()



asyncData hook

搞定store action,接著就是如何在asyncData()內catch到reject()了!


上code說明

async asyncData({ store, params, error }) {
  let article
  await store
    .dispatch('articles/loadCurrentArticle', params.articleId)
    .then(() => {
      article = {
        ...store.getters['articles/getCurrentArticle'],
      }
    })
    .catch(() => {
      error({ statusCode: 404, message: 'Article not found' })
    })
  return {
    id: article.id,
    title: article.title,
    publicationDate: article.publicationDate,
    category: article.category,
    tags: article.tags,
    description: article.description,
    content: article.content,
    updatedDate: article.updatedDate ? article.updatedDate : '',
  }
},


把route的articleId parameter當作payload,store.dispatch載入當前文章內容的action


由於這個action return Promise,所以你可以.then()和.catch()


在.then()中使用store.getters取得當前文章內容


在.catch()中,呼叫Nuxt context error function,輸入statusCode和message


就這樣,之後每次在render文章頁面前,只要Nuxt接收到error訊息,都會自動跳轉至error.vue的畫面


而且錯誤的路徑還會保留在瀏覽器上唷! (若用redirect方法就無法保留錯誤路徑)


完成!👏👏👏





至於為何.then()內不直接取完值後return下面那一串data呢?感覺很合理啊!


因為會讀不到,return的data object無法正確吐給data()去配對...


我也不知道為什麼!求大神解釋了!


© 2021 Hamsterism. All rights reserved github