哈囉,大家好!在前面的文章中,我們已經成功地建立了 Nuxt 的開發環境,並且深入了解了 Nuxt 的前端結構,特別是 app.vue 和 layouts/default.vue 的作用。現在,是時候讓我們的應用程式真正「活」起來,與後端進行互動了。
今天,我們將探討如何在 Nuxt 中使用 Axios 來整合 Laravel 後端 API,實現資料的動態渲染。透過這個過程,我們的個人財務管理系統將能夠真正地為使用者提供服務,從後端獲取資料,並在前端呈現。
在現代的前端開發中,與後端進行 HTTP 請求已經是不可或缺的一部分。Axios 是一個基於 Promise 的 HTTP 客戶端,可以用在瀏覽器和 Node.js 中。相比於原生的 fetch API,Axios 提供了更直觀的語法和更豐富的功能,例如自動轉換 JSON、攔截請求和回應、取消請求等。
使用 Axios,我們可以:
在 Nuxt 中,我們可以輕鬆地整合 Axios。有兩種主要的方式:
首先,我們需要在 Nuxt 專案中安裝 @nuxtjs/axios 模組。
確保你在 Nuxt 容器內(如果不確定,請執行 docker-compose exec frontend sh 進入容器)。
npm install @nuxtjs/axios
在安裝完畢後,我們需要在 nuxt.config.js 中添加 Axios 模組的設定。
export default {
// 其他設定...
modules: [
'@nuxtjs/axios',
],
axios: {
baseURL: 'http://api.localhost', // 後端 API 的基礎 URL
},
// 其他設定...
}
這樣,我們就可以在 Nuxt 中使用 Axios,並且所有的請求都會以 baseURL 作為基礎。
為了方便在不同環境(如開發、測試、正式)中切換,我們可以使用環境變數來管理 baseURL。
在專案根目錄下建立一個 .env 檔案(如果尚未建立):
API_BASE_URL=http://api.localhost
export default {
// 其他設定...
publicRuntimeConfig: {
axios: {
browserBaseURL: process.env.API_BASE_URL,
},
},
privateRuntimeConfig: {
axios: {
baseURL: process.env.API_BASE_URL,
},
},
// 其他設定...
}
這樣,我們就可以在不同的環境中,透過設定環境變數來調整 baseURL。
現在,我們已經設定好了 Axios,接下來,我們將在頁面或元件中使用 Axios 來從後端獲取資料。
我們以交易紀錄頁面為例,實現從後端獲取交易資料並在前端渲染。
(1)編輯 pages/transactions.vue:
<template>
<div>
<h2 class="text-2xl font-bold mb-4">交易紀錄</h2>
<ul>
<li v-for="transaction in transactions" :key="transaction.id">
<p>{{ transaction.description }} - {{ transaction.amount }} 元</p>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
transactions: [],
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/transactions')
this.transactions = response.data
} catch (error) {
console.error('Error fetching transactions:', error)
}
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
在前一篇文章中,我們提到應該統一 API 的回應格式,方便前後端協作。假設後端的回應格式為:
{
"status": "success",
"data": [
{
"id": 1,
"description": "購買書籍",
"amount": 500,
// 其他欄位...
},
// 更多交易...
]
}
那麼,我們需要在前端提取 data 中的資料。
async mounted() {
try {
const response = await this.$axios.get('/api/transactions')
if (response.data.status === 'success') {
this.transactions = response.data.data
} else {
console.error('API returned an error:', response.data.message)
}
} catch (error) {
console.error('Error fetching transactions:', error)
}
}
為了提升使用者體驗,我們應該在發生錯誤時,向使用者顯示友好的提示。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">交易紀錄</h2>
<div v-if="error" class="text-red-500">
{{ error }}
</div>
<ul v-else>
<li v-for="transaction in transactions" :key="transaction.id">
<p>{{ transaction.description }} - {{ transaction.amount }} 元</p>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
transactions: [],
error: null,
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/transactions')
if (response.data.status === 'success') {
this.transactions = response.data.data
} else {
this.error = response.data.message || '無法獲取交易資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching transactions:', error)
}
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
現在,如果發生錯誤,使用者將看到友好的錯誤訊息。
除了在頁面中,我們也可以在元件中使用 Axios。為了提高程式碼的重用性和可維護性,我們可以將交易列表提取為一個元件。
<template>
<div>
<div v-if="error" class="text-red-500">
{{ error }}
</div>
<ul v-else>
<li v-for="transaction in transactions" :key="transaction.id">
<p>{{ transaction.description }} - {{ transaction.amount }} 元</p>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
transactions: [],
error: null,
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/transactions')
if (response.data.status === 'success') {
this.transactions = response.data.data
} else {
this.error = response.data.message || '無法獲取交易資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching transactions:', error)
}
},
}
</script>
<style scoped>
/* 元件專屬的樣式 */
</style>
<template>
<div>
<h2 class="text-2xl font-bold mb-4">交易紀錄</h2>
<TransactionList />
</div>
</template>
<script>
import TransactionList from '~/components/TransactionList.vue'
export default {
components: {
TransactionList,
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
這樣,我們就成功地將交易列表提取為可重用的元件,未來在其他頁面中也可以使用。
在實際的應用中,交易紀錄應該只允許已登入的使用者訪問。因此,我們需要實作身份驗證機制,並在前端進行保護。
為了不讓本文過於冗長,我們假設後端已經實作了登入和註冊的 API,並使用 JWT(JSON Web Token)進行身份驗證。
我們可以使用 Axios 攔截器,在請求發送前自動在 Header 中添加 Token。
在 plugins/ 目錄下建立 axios.js:
export default function ({ $axios, store }) {
$axios.onRequest((config) => {
const token = store.state.auth.token
if (token) {
config.headers.common.Authorization = `Bearer ${token}`
}
})
}
export default {
// 其他設定...
plugins: [
'~/plugins/axios.js',
],
// 其他設定...
}
<template>
<div class="max-w-md mx-auto">
<h2 class="text-2xl font-bold mb-4">登入</h2>
<form @submit.prevent="login">
<div class="mb-4">
<label class="block">電子郵件</label>
<input v-model="email" type="email" class="border p-2 w-full" required />
</div>
<div class="mb-4">
<label class="block">密碼</label>
<input v-model="password" type="password" class="border p-2 w-full" required />
</div>
<div v-if="error" class="text-red-500 mb-4">
{{ error }}
</div>
<button type="submit" class="bg-blue-600 text-white p-2">登入</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
email: '',
password: '',
error: null,
}
},
methods: {
async login() {
try {
const response = await this.$axios.post('/api/login', {
email: this.email,
password: this.password,
})
if (response.data.status === 'success') {
this.$store.commit('auth/setToken', response.data.token)
this.$router.push('/')
} else {
this.error = response.data.message || '登入失敗。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error during login:', error)
}
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
我們需要使用 Vuex(或 Pinia)來管理使用者的身份驗證狀態。
在 store/ 目錄下建立 auth.js:
export const state = () => ({
token: null,
})
export const mutations = {
setToken(state, token) {
state.token = token
},
clearToken(state) {
state.token = null
},
}
export const getters = {
isAuthenticated(state) {
return !!state.token
},
}
我們可以使用 Nuxt 的中介層(middleware)來保護需要身份驗證的頁面。
在 middleware/ 目錄下建立 auth.js:
export default function ({ store, redirect }) {
if (!store.getters['auth/isAuthenticated']) {
return redirect('/login')
}
}
例如,在 pages/transactions.vue 中:
export default {
middleware: 'auth',
// 其他設定...
}
這樣,未登入的使用者將被自動導向登入頁面。
現在,我們已經整合了後端 API,並實作了基本的身份驗證功能。讓我們來測試一下:
在 Nuxt 中,我們可以使用 asyncData 或 fetch 方法來在頁面渲染之前獲取資料,這對於需要 SSR(伺服器端渲染)的頁面特別有用。
export default {
async asyncData({ $axios }) {
try {
const response = await $axios.get('/api/transactions')
if (response.data.status === 'success') {
return {
transactions: response.data.data,
}
} else {
return {
error: response.data.message || '無法獲取交易資料。',
}
}
} catch (error) {
return {
error: '伺服器發生錯誤,請稍後再試。',
}
}
},
data() {
return {
transactions: [],
error: null,
}
},
}
為了避免在每個請求中都重複處理錯誤,我們可以在 Axios 攔截器中統一處理。
export default function ({ $axios, store, redirect }) {
$axios.onRequest((config) => {
const token = store.state.auth.token
if (token) {
config.headers.common.Authorization = `Bearer ${token}`
}
})
$axios.onError((error) => {
const code = parseInt(error.response && error.response.status)
if (code === 401) {
store.commit('auth/clearToken')
redirect('/login')
}
})
}
這樣,當遇到 401 未授權錯誤時,將自動清除 Token 並導向登入頁面。
今天,我們成功地整合了後端 API,並在 Nuxt 中使用 Axios 實現了資料的動態渲染。同時,我們也實作了基本的身份驗證功能,並學習了如何保護受限的頁面。
透過這些步驟,我們的個人財務管理系統已經初具規模,使用者可以登入系統,查看自己的交易紀錄,並享受良好的使用者體驗。
接下來,我們可以:
希望這篇文章能夠對你有所幫助,讓我們一起繼續學習和進步,打造出更加優秀的應用程式!
感謝你的閱讀,如果你有任何問題或建議,歡迎在下方留言討論。我們下篇見!