Nuxt 中的 Axios 入門
已發表: 2022-03-10ayncData
和fetch
方法在使用 Axios 的服務器端獲取數據以及這兩種方法之間的區別。 最後,我們將學習如何使用 Auth 模塊向我們的應用程序添加身份驗證。Nuxt.js 提供了一個 Axios 模塊,以便與您的應用程序輕鬆集成。 Axios 是一個基於 Promise 的 HTTP 客戶端,可以在瀏覽器和 Node.js 環境中工作,或者更簡單地說,它是一種在客戶端應用程序和 Node.js 環境中發出請求(例如 API 調用)的工具。
在本教程中,我們將學習如何使用 Axios 模塊以及如何使用 asyncData 和 fetch 在服務器端發出請求。 這兩種方法在服務器端發出請求,但它們有一些區別,我們也將介紹。 最後,我們將學習如何使用 auth 模塊和 auth 中間件執行身份驗證和保護頁面/路由。
本文需要 Nuxtjs 和 Vuejs 的基本知識,因為我們將在此基礎上進行構建。 對於那些沒有 Vuejs 經驗的人,我建議你先從他們的官方文檔和 Nuxt 官方頁面開始,然後再繼續本文。
什麼是 Nuxt.js Axios 模塊?
根據官方文檔,
“這是與 Nuxt.js 的安全且簡單的 Axios 集成。”
以下是它的一些特點:
- 自動設置客戶端和服務器端的基本 URL。
- SSR 中的代理請求標頭(對身份驗證有用)。
- 獲取樣式請求。
- 在發出請求時與 Nuxt.js 進度條集成。
要在應用程序中使用 axios 模塊,您必須首先使用npm
或yarn
安裝它。
紗
yarn add @nuxtjs/axios
新PM
npm install @nuxtjs/axios
將其添加到您的nuxt.config.js
文件中:
modules: [ '@nuxtjs/axios', ], axios: { // extra config eg // BaseURL: 'https://link-to-API' }
modules
數組接受 Nuxt.js 模塊的列表,例如 dotenv、auth,在本例中為 Axios。 我們所做的是通知我們的應用程序我們將使用 Axios 模塊,我們使用@nuxtjs/axios
引用該模塊。 然後是axios
屬性,它是一個配置對象,例如客戶端和服務器端的 baseURL。
現在,您可以通過調用this.$axios.method
或this.$axios.$method
從應用程序的任何位置訪問 Axios。 where方法可以是get
、 post
或delete
。
使用 Axios 發出您的第一個請求
對於本教程,我在 Github 上整理了一個簡單的應用程序。 存儲庫包含兩個文件夾,start 和 finish, start文件夾包含您直接進入教程所需的所有內容。 完成文件夾包含我們將要構建的完整版本。
克隆 repo 並打開start
文件夾後,我們需要在package.json
文件中安裝所有包,因此打開終端並運行以下命令:
npm install
完成後,我們可以使用npm run dev
命令啟動我們的應用程序。 這是您訪問localhost:3000
時應該看到的內容。
接下來我們要做的是在應用程序的根文件夾中創建一個.env
文件,並將我們的 API URL 添加到其中。 在本教程中,我們將使用為收集用戶報告而構建的示例 API。
API_URL=https://ireporter-endpoint.herokuapp.com/api/v2/
這樣,我們不必將 API 硬編碼到我們的應用程序中,這對於使用兩個 API(開發和生產)很有用。
下一步是打開我們的nuxt.config.js
文件並將環境變量添加到我們上面添加的 axios 配置中。
/* ** Axios module configuration */ axios: { // See https://github.com/nuxt-community/axios-module#options baseURL: process.env.API_URL, },
在這裡,我們告訴 Nuxt.js,只要我們使用這個 Axios 模塊,就將這個baseURL
用於我們的客戶端和服務器端請求。
現在,要獲取報告列表,讓我們打開index.vue
文件並將以下方法添加到腳本部分。
async getIncidents() { let res = await this.$store.dispatch("getIncidents"); this.incidents = res.data.data.incidents; }
我們所做的是創建一個我們稱之為getIncidents()
的異步函數,我們可以從名稱中看出它的作用——它使用 Vuex 存儲操作方法this.$store.dispatch
獲取事件列表。 我們將此操作的響應分配給我們的事件屬性,以便我們能夠在組件中使用它。
我們想在組件掛載時調用getIncidents()
方法。 我們可以使用mounted
的鉤子來做到這一點。
mounted() { this.getIncidents() }
mounted()
是一個生命週期鉤子,在組件掛載時被調用。 這將導致在組件掛載時調用 API。 現在,讓我們進入我們商店中的index.js
文件並創建這個動作,我們將從這裡發出我們的 Axios 請求。
export const actions = { async getIncidents() { let res = await this.$axios.get('/incidents') return res; } }
在這裡,我們創建了一個名為getIncidents
的操作,它是一個異步函數,然後我們等待來自服務器的響應並返回此響應。 此操作的響應被發送回index.vue
文件中的getIncidents()
方法。
如果我們刷新我們的應用程序,我們現在應該能夠看到頁面上呈現的一長串事件。
我們已經使用 Axios 發出了第一個請求,但我們不會就此止步,我們將嘗試asyncData
和fetch
以查看它們與使用 Axios 之間的差異。
異步數據
AsyncData 在服務器端獲取數據,並在加載頁面組件之前調用它。 它無權訪問this
,因為它是在創建頁面組件數據之前調用的。 this
僅在調用created
鉤子後可用,因此 Nuxt.js 會自動將返回的數據合併到組件的數據中。
使用asyncData
有利於 SEO,因為它可以在服務器端獲取您網站的內容,還有助於更快地加載內容。 請注意, asyncData
方法只能在應用程序的 pages 文件夾中使用,因為它在 components 文件夾中不起作用。 這是因為在創建組件之前調用了asyncData
掛鉤。
讓我們將asyncData
添加到我們的index.vue
文件中,並觀察我們的事件數據加載速度。 在我們的components屬性之後添加以下代碼,讓我們擺脫掛載的鉤子。
async asyncData({ $axios }) { let { data } = await $axios.get("/incidents"); return { incidents: data.data.incidents }; }, // mounted() { // this.getIncidents(); // },
在這裡, asyncData
方法接受來自上下文$axios
的屬性。 我們使用這個屬性來獲取事件列表,然後返回值。 這個值會自動注入到我們的組件中。 現在,您可以注意到,如果您刷新頁面並且在任何時候都不會出現要呈現的事件,那麼您的內容加載速度有多快。
拿來
Fetch 方法也用於在服務器端發出請求。 它在生命週期中創建的鉤子之後調用,這意味著它可以訪問組件的數據。 與asyncData
方法不同,fetch 方法可以在所有.vue文件中使用,並與Vuex 存儲一起使用。 這意味著如果您的數據函數中有以下內容。
data() { return { incidents: [], id: 5, gender: 'male' }; }
您可以通過調用this.id
或this.gender
輕鬆修改id或性別。
使用 Axios 作為插件
在使用 Axios 開發的過程中,您可能會發現您需要額外的配置,例如為您的請求創建實例和攔截器,以便您的應用程序可以按預期工作,幸運的是,我們可以通過將 Axios 擴展為插件來做到這一點。
要擴展axios
,您必須在您的plugins
文件夾中創建一個插件(例如axios.js )。
export default function ({ $axios, store, redirect }) { $axios.onError(error => { if (error.response && error.response.status === 500) { redirect('/login') } }) $axios.interceptors.response.use( response => { if (response.status === 200) { if (response.request.responseURL && response.request.responseURL.includes('login')) { store.dispatch("setUser", response); } } return response } ) }
這是我為 Nuxt 應用程序編寫的插件示例。 在這裡,您的函數接受$axios
的上下文對象、 store
和redirect
,我們將在配置插件時使用它們。 我們要做的第一件事是使用$axios.onError
狀態為500
的錯誤並將用戶重定向到登錄頁面。
我們還有一個攔截器,它攔截我們在應用程序中所做的每個請求響應,檢查我們得到的響應狀態是否為200
。 如果這是真的,我們繼續檢查是否有response.request.responseURL
以及它是否包含登錄。 如果這是真的,我們然後使用我們商店的調度方法發送這個響應,然後它在我們的狀態中發生變化。
將此插件添加到您的nuxt.config.js
文件中:
plugins: [ '~/plugins/axios' ]
完成此操作後,您的 Axios 插件將攔截您發出的任何請求並檢查您是否為它定義了特殊情況。
Auth 模塊簡介
auth 模塊用於為 Nuxt 應用程序執行身份驗證,可以使用$this.auth
從應用程序中的任何位置訪問。 它也可用於上下文對像中的fetch
、 asyncData
、 middleware
和NuxtInitServer
作為$auth
。
context
提供了從 Nuxt 到 Vue 組件的附加對象/參數,並且在像上面提到的那些特殊的 nuxt 生命週期區域中可用。
要在應用程序中使用 auth 模塊,您必須使用yarn
或npm
安裝它。
紗
yarn add @nuxtjs/auth
新PM
npm install @nuxtjs/auth
將其添加到您的nuxt.config.js
文件中。
modules: [ '@nuxtjs/auth' ], auth: { // Options }
auth屬性接受一個屬性列表,例如strategies
和redirect
。 在這裡, strategies
接受您首選的身份驗證方法,可以是:
-
local
對於用戶名/電子郵件和基於密碼的流程。 -
facebook
用於使用 Facebook 帳戶作為身份驗證手段。 -
Github
用於驗證具有 Github 帳戶的用戶。 -
Google
用於使用 Google 帳戶對用戶進行身份驗證。 - 授權0
- Laravel 護照
redirect 屬性接受以下鏈接對象:
-
login
如果需要登錄,用戶將被重定向到此鏈接。 -
logout
如果註銷後當前路由受到保護,用戶將被重定向到此處。 -
home
用戶登錄後會被重定向到這裡。
現在,讓我們將以下內容添加到我們的nuxt.config.js
文件中。
/* ** Auth module configuration */ auth: { redirect: { login: '/login', logout: '/', home: '/my-reports' }, strategies: { local: { endpoints: { login: { url: "/user/login", method: "post", propertyName: "data.token", }, logout: false, user: false, }, tokenType: '', tokenName: 'x-auth', autoFetchUser: false }, }, }
請注意,當上述選項中提供了user
端點時, auth
方法效果最好。
在auth
配置對像中,我們有一個redirect
選項,在該選項中,我們將登錄路由設置為/login
,將註銷路由設置為/
,將主路由設置為/my-reports
,這些都將按預期運行。 我們還有一個tokenType
屬性,它代表我們 Axios 請求標頭中的授權類型。 它默認設置為Bearer
,並且可以更改為與您的 API 一起使用。
對於我們的 API,沒有令牌類型,這就是我們將其保留為空字符串的原因。 tokenName
代表 Axios 請求中標頭內的授權名稱(或您要附加令牌的標頭屬性)。
默認情況下,它設置為Authorization
,但對於我們的 API,授權名稱是x-auth
。 autoFetchUser
屬性用於在登錄後使用user
端點屬性啟用用戶獲取對象。 默認情況下為true
,但我們的 API 沒有user
端點,因此我們將其設置為false
。
對於本教程,我們將使用本地策略。 在我們的策略中,我們有用於登錄、用戶和註銷端點的本地選項,但在我們的例子中,我們只會使用*login*
選項,因為我們的演示 API 沒有*logout*
端點並且我們的用戶對象正在返回當*login*
成功時。
注意: auth
模塊沒有註冊端點選項,這意味著我們將以傳統方式註冊並將用戶重定向到登錄頁面,我們將使用this.$auth.loginWith
執行身份驗證。 這是用於驗證您的用戶的方法。 它接受一個“策略”(例如local
)作為第一個參數,然後是一個執行此身份驗證的對象。 看看下面的例子。
let data { email: '[email protected]', password: '123456' } this.$auth.loginWith('local', { data })
使用認證模塊
現在我們已經配置了我們的 auth 模塊,我們可以進入我們的註冊頁面。 如果您訪問/register
頁面,您應該會看到一個註冊表單。
讓我們通過添加以下代碼來使這個表單起作用:
methods: { async registerUser() { this.loading = true; let data = this.register; try { await this.$axios.post("/user/create", data); this.$router.push("/login"); this.loading = false; this.$notify({ group: "success", title: "Success!", text: "Account created successfully" }); } catch (error) { this.loading = false; this.$notify({ group: "error", title: "Error!", text: error.response ? error.response.data.error : "Sorry an error occured, check your internet" }); } } }
在這裡,我們有一個名為registerUser
的異步函數,它與模板中的點擊事件相關聯,並向端點/user/create
發出包裝在try/catch 塊中的 Axios 請求。 這將重定向到/login
頁面並通知用戶註冊成功。 我們還有一個 catch 塊,如果請求不成功,它會提醒用戶任何錯誤。
如果註冊成功,您將被重定向到登錄頁面。
在這裡,我們將使用身份驗證方法this.$auth.loginWith('local', loginData)
之後我們將使用this.$auth.setUser(userObj)
在我們的auth
實例中設置用戶。
為了讓登錄頁面正常工作,讓我們將以下代碼添加到我們的login.vue
文件中。
methods: { async logIn() { let data = this.login; this.loading = true; try { let res = await this.$auth.loginWith("local", { data }); this.loading = false; let user = res.data.data.user; this.$auth.setUser(user); this.$notify({ group: "success", title: "Success!", text: "Welcome!" }); } catch (error) { this.loading = false; this.$notify({ group: "error", title: "Error!", text: error.response ? error.response.data.error : "Sorry an error occured, check your internet" }); } } }
我們使用身份驗證方法this.$auth.loginWith('local, loginData)
創建了一個名為logIn
的異步函數。 如果此登錄嘗試成功,我們然後使用this.$auth.setUser(userInfo)
將用戶數據分配給我們的 auth 實例,並將用戶重定向到/my-report
頁面。
您現在可以使用this.$auth.user
或使用 Vuex 使用this.$store.state.auth.user
獲取用戶數據,但這還不是全部。 auth
實例包含一些其他屬性,如果您登錄或使用 Vue 開發工具檢查您的狀態,您可以看到這些屬性。
如果您將this.$store.state.auth
到控制台,您將看到:
{ "auth": { "user": { "id": "d7a5efdf-0c29-48aa-9255-be818301d602", "email": "[email protected]", "lastName": "Xo", "firstName": "Tm", "othernames": null, "isAdmin": false, "phoneNumber": null, "username": null }, "loggedIn": true, "strategy": "local", "busy": false } }
auth
實例包含一個loggedIn
屬性,該屬性可用於在應用程序的 nav/header 部分中的經過身份驗證的鏈接之間切換。 它還包含一個策略方法,用於說明實例正在運行的策略類型(例如本地)。
現在,我們將利用這個loggedIn
屬性來安排我們的nav
鏈接。 將您的navBar
組件更新為以下內容:
<template> <header class="header"> <div class="logo"> <nuxt-link to="/"> <Logo /> </nuxt-link> </div> <nav class="nav"> <div class="nav__user" v-if="auth.loggedIn"> <p>{{ auth.user.email }}</p> <button class="nav__link nav__link--long"> <nuxt-link to="/report-incident">Report incident</nuxt-link> </button> <button class="nav__link nav__link--long"> <nuxt-link to="/my-reports">My Reports</nuxt-link> </button> <button class="nav__link" @click.prevent="logOut">Log out</button> </div> <button class="nav__link" v-if="!auth.loggedIn"> <nuxt-link to="/login">Login</nuxt-link> </button> <button class="nav__link" v-if="!auth.loggedIn"> <nuxt-link to="/register">Register</nuxt-link> </button> </nav> </header> </template> <script> import { mapState } from "vuex"; import Logo from "@/components/Logo"; export default { name: "nav-bar", data() { return {}; }, computed: { ...mapState(["auth"]) }, methods: { logOut() { this.$store.dispatch("logOut"); this.$router.push("/login"); } }, components: { Logo } }; </script> <style></style>
在我們的模板部分,我們有幾個鏈接到應用程序的不同部分,我們現在使用auth.loggedIn
來根據身份驗證狀態顯示適當的鏈接。 我們有一個註銷按鈕,它有一個帶有logOut()
函數的click
事件。 我們還顯示從 auth 屬性獲取的用戶電子郵件,該屬性使用mapState
方法從我們的 Vuex 存儲訪問,該方法將我們的狀態身份驗證映射到 nav 組件的計算屬性。 我們還有一個logout
方法,它調用我們的 Vuex 操作logOut
並將用戶重定向到login
頁面。
現在,讓我們繼續更新我們的商店以進行logOut
操作。
export const actions = { // .... logOut() { this.$auth.logout(); } }
logOut
操作調用 auth logout
方法,該方法清除用戶數據、從localStorage
中刪除令牌並將loggedIn
設置為false
。
/my-reports
和report-incident
之類的路線不應該對客人可見,但在我們的應用程序中,情況並非如此。 Nuxt 沒有可以保護您的路線的導航守衛,但它有 auth 中間件。 它使您可以自由地創建自己的中間件,以便您可以對其進行配置以按照您想要的方式工作。
可以通過兩種方式設置:
- 每條路線。
- 全局用於
nuxt.config.js
文件中的整個應用程序。
router: { middleware: ['auth'] }
此auth
中間件適用於您的auth
實例,因此您無需在中間件文件夾中創建auth.js
文件。
現在讓我們將此中間件添加到我們的my-reports.vue
和report-incident.vue
文件中。 將以下代碼行添加到每個文件的腳本部分。
middleware: 'auth'
現在,我們的應用程序將檢查嘗試訪問這些路由的用戶是否具有true
的auth.loggedIn
值。 它將使用我們的身份驗證配置文件中的重定向選項將它們重定向到登錄頁面——如果您沒有登錄並且您嘗試訪問/my-report
或report-incident
,您將被重定向到/login
。
如果你去/report-incidents
,這就是你應該看到的。
此頁面用於添加事件,但現在表單不會將事件發送到我們的服務器,因為當用戶嘗試提交表單時我們沒有調用服務器。 為了解決這個問題,我們將添加一個reportIncident
方法,當用戶單擊Report時將調用該方法。 我們將在組件的腳本部分擁有它。 此方法會將表單數據發送到服務器。 使用以下內容更新您的report-incident.vue
文件:
<template> <section class="report"> <h1 class="report__heading">Report an Incident</h1> <form class="report__form"> <div class="input__container"> <label for="title" class="input__label">Title</label> <input type="text" name="title" v-model="incident.title" class="input__field" required /> </div> <div class="input__container"> <label for="location" class="input__label">Location</label> <input type="text" name="location" v-model="incident.location" required class="input__field" /> </div> <div class="input__container"> <label for="comment" class="input__label">Comment</label> <textarea name="comment" v-model="incident.comment" class="input__area" cols="30" rows="10" required ></textarea> </div> <input type="submit" value="Report" class="input__button" @click.prevent="reportIncident" /> <p class="loading__indicator" v-if="loading">Please wait....</p> </form> </section> </template> <script> export default { name: "report-incident", middleware: "auth", data() { return { loading: false, incident: { type: "red-flag", title: "", location: "", comment: "" } }; }, methods: { async reportIncident() { let data = this.incident; let formData = new FormData(); formData.append("title", data.title); formData.append("type", data.type); formData.append("location", data.location); formData.append("comment", data.comment); this.loading = true; try { let res = await this.$store.dispatch("reportIncident", formData); this.$notify({ group: "success", title: "Success", text: "Incident reported successfully!" }); this.loading = false; this.$router.push("/my-reports"); } catch (error) { this.loading = false; this.$notify({ group: "error", title: "Error!", text: error.response ? error.response.data.error : "Sorry an error occured, check your internet" }); } } } }; </script> <style> </style>
在這裡,我們有一個表單,其中包含標題、位置和評論的輸入字段,並使用v-model
進行雙向數據綁定。 我們還有一個帶有點擊事件的submit
按鈕。 在腳本部分,我們有一個reportIncident
方法,它收集表單中提供的所有信息,並使用 FormData 發送到我們的服務器,因為 API 也被設計為接受圖像和視頻。
此formData
使用 dispatch 方法附加到 Vuex 操作,如果請求成功,您將被重定向到/my-reports
並通知您此請求成功,否則,您將收到錯誤消息的錯誤通知.
此時,我們的商店中還沒有reportIncident
操作,因此在您的瀏覽器控制台中,如果您嘗試單擊此頁面上的提交,您會看到一個錯誤。
要解決此問題,請將reportIncident操作添加到您的index.js
文件中。
export const actions = { // ... async reportIncident({}, data) { let res = await this.$axios.post('/incident/create', data) return res; } }
在這裡,我們有一個reportIncident
函數,它接收一個空的上下文對象和我們從表單發送的數據。 然後將此數據附加到創建事件並返回到我們的report-incident.vue
文件的post
請求中。
此時,您應該能夠使用表單添加報告,之後您將被重定向到/my-reports
頁面。
此頁面應顯示用戶創建的事件列表,但現在它只顯示我們在上面看到的內容,讓我們繼續解決這個問題。
我們將使用我們學到的fetch
方法來獲取這個列表。 使用以下內容更新您的my-reports.vue
文件:
<script> import incidentCard from "@/components/incidentCard.vue"; export default { middleware: "auth", name: "my-reports", data() { return { incidents: [] }; }, components: { incidentCard }, async fetch() { let { data } = await this.$axios.get("/user/incidents"); this.incidents = data.data; } }; </script>
在這裡,我們使用fetch
方法來獲取用戶特定的事件並將響應分配給我們的事件數組。
如果您在添加事件後刷新頁面,您應該會看到類似這樣的內容。
此時,我們會注意到fetch
方法和asyncData
加載數據的方式有所不同。
結論
到目前為止,我們已經了解了 Axios 模塊及其所有功能。 我們還了解了有關 asyncData 的更多信息,以及儘管它們存在差異,但我們如何將它們一起獲取。 我們還學習瞭如何使用 auth 模塊在我們的應用程序中執行身份驗證,以及如何使用 auth 中間件來保護我們的路由。 以下是一些有用的資源,它們更多地討論了我們所涵蓋的所有內容。
- Nuxjs 中的元標記入門。
- 在 Nuxt 中使用 dotenv 模塊。
- 在您的 Nuxt 應用程序中使用 Fetch。
- 開始使用異步數據。
資源
- “認證模塊”,NuxtJS.org
- “Axios 模塊:簡介”,NuxtJS.org
-
FormData
, MDN 網絡文檔 - “API:
asyncData
方法”,NuxtJS.org - “Vue 實例:生命週期圖”,VueJS.org
- “了解
fetch
在 Nuxt 2.12 中的工作原理”,NuxtJS.org