在前面“多層次動態選單”的文章中,我們把選單資料放在Javascript中進行描述,可以發現整支Javascript中邏輯運算的部分極少,大多都是描述頁面資料的部分,也提到在未來我們應該可以將這部分的資料獨立拉出來至一個檔案,甚至在前、後端分離的網頁中,頁面資料大多由後端API提供,如此一來我們就不得不了解一下Axios的部分了!
關於Vue與後端API擷取資料的部分,比較熱門的做法其實有兩個:
大家看今天的標題應該也知道,我們應該不會去介紹vue-resource,原因是因為他在Vue 2.0之後就已經不再維護,連官方都推薦使用axios,所以這篇文章我們就先用axios來示範如何取得外部的json檔,其實意思也類似打api只是這個api現在是個固定的json檔罷了。
本次我們準備的檔案有兩個,一個是HTML並且把Javascript撰寫在HTML頁面中,另外就是外部的json檔,需要注意json檔跟Javascript中的物件還是有一些差異,因為json檔不管是key或value都需要用雙引號包覆起來,我們製作的Sample json檔的內容如下:
{
"title": "網站框架整理",
"frameworks": [
{
"type": "frontend",
"content":[
{
"name": "Vue",
"version": "2.x"
},
{
"name": "React",
"version": "16.x"
},
{
"name": "React",
"version": "9.x"
}
]
},
{
"type": "backend",
"content":[
{
"name": "Laravel",
"version": "7.x"
},
{
"name": "CakePHP",
"version": "4.x"
},
{
"name": "Django",
"version": "3.x"
}
]
}
]
}
接下來我們就要透過網頁將其讀入,並且列示於畫面上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<style>
.noteText {
font-size: 12px;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="item in state.frameworks">
{{ item.type }}
<ul>
<li v-for="itemDetail in item.content">
{{ itemDetail.name }} ( {{ itemDetail.version }} )
</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
<script>
const { reactive, onBeforeMount } = Vue;
const app = {
setup(){
const state = reactive({
frameworks: null,
})
onBeforeMount(() => {
axios.get("frameworks.json").then(function (response) {
state.frameworks = response.data.frameworks;
}).catch(function (response) {
console.log(response);
})
})
return { state };
}
}
const myVue = Vue.createApp(app).mount("#app");
</script>
可以看到上面程式HTML的部分我們加入了兩行的Plugin,因為axios並不是vue或HTML內建的功能,所以必須要透過外掛載入(也就是第HTML第8行)。
在Javascript 第8~12行則是axios的語法,我們希望在vue onBeforeMount的階段去執行載入json的動作,這也是前一篇文章介紹過的一個生命週期,可以看到頁面上的效果如下:
如此一來我們就成功地把資料與頁面分離,未來這個部分也會成為前後端分離開發方式的基石,有了上面範例的成功,我們想到之前的多層式選單案例,當時就覺得頁面中的Javascript中有太多的json資料,我們也試著把三層式選單的案例改寫,將資料獨立出來看看寫法上有沒有什麼不同!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<style>
.noteText {
font-size: 12px;
}
</style>
</head>
<body>
<div id="app">
<h3>Please make your decision:</h3>
<p>
Gender:
<select v-model="state.genderIdx">
<option v-for="(item, index) in state.clothes" :value="index">
{{item.gender}}
</option>
</select>
</p>
<p>
Type:
<select v-model="state.partIdx">
<option v-for="(item, index) in pickTypes" :value="index">
{{item.part}}
</option>
</select>
</p>
<p>
Product:
<select v-model="state.itemIdx">
<option v-for="(item, index) in pickContents" :value="index">
{{item.product}}
</option>
</select>
</p>
</div>
</body>
</html>
<script>
const { reactive, onBeforeMount, computed } = Vue;
const app = {
setup(){
const state = reactive({
genderIdx: 0, // 記錄第一層選單的被選取項目
partIdx: 0, // 記錄第二層選單的被選取項目
itemIdx: 0, // 記錄第三層選單的被選取項目
clothes: null,
})
onBeforeMount(() => {
axios.get("clothes.json").then(function (response) {
state.clothes = response.data.clothes;
}).catch(function (response) {
console.log(response);
})
})
const pickTypes = computed(() => {
return (state.clothes === null ? "" : state.clothes[state.genderIdx].types);
})
const pickContents = computed(() => {
return (state.clothes === null ? "" : state.clothes[state.genderIdx].types[state.partIdx].contents);
})
return { state, pickTypes, pickContents };
}
}
const myVue = Vue.createApp(app).mount("#app");
</script>
{
"clothes":[
{
"gender": "男",
"types": [
{
"part": "上衣類",
"contents": [
{ "product": "短袖/背心" },
{ "product": "長袖" },
{ "product": "立領/高領" },
{ "product": "針織衫" },
{ "product": "休閒襯衫" },
{ "product": "商務襯衫" },
{ "product": "法蘭絨系列" },
{ "product": "厚棉系列" }
]
},
{
"part": "外套類",
"contents": [
{ "product": "休閒外套" },
{ "product": "Fleece系列" },
{ "product": "極輕羽絨" },
{ "product": "極暖羽絨" }
]
},
{
"part": "下身類",
"contents": [
{ "product": "短/七分褲" },
{ "product": "九分/束口褲" },
{ "product": "休閒長褲" },
{ "product": "牛仔褲" },
{ "product": "保暖褲" }
]
},
{
"part": "家居服",
"contents": [
{ "product": "家居套裝" },
{ "product": "家居褲" },
{ "product": "家居毯" }
]
}
]
},
{
"gender": "女",
"types": [
{
"part": "上衣類",
"contents": [
{ "product": "印花短T" },
{ "product": "印花長T" },
{ "product": "短袖/背心" },
{ "product": "七分/長袖" },
{ "product": "長版上衣" },
{ "product": "針織衫" },
{ "product": "polo衫" },
{ "product": "Pima棉" }
]
},
{
"part": "外套類",
"contents": [
{ "product": "休閒外套" },
{ "product": "Fleece系列" },
{ "product": "極輕羽絨" },
{ "product": "極暖羽絨" }
]
},
{
"part": "下身類",
"contents": [
{ "product": "休閒短褲" },
{ "product": "七/九分褲" },
{ "product": "牛仔系列" },
{ "product": "寬褲系列" },
{ "product": "休閒長褲" },
{ "product": "裙子" },
{ "product": "連身/吊帶褲" },
{ "product": "緊身褲" },
{ "product": "裙子" }
]
},
{
"part": "洋裝",
"contents": [
{ "product": "洋裝" },
{ "product": "家居褲" },
{ "product": "吊帶裙" }
]
}
]
}
]
}
結果可以參考範例檔,如此一來我們就能將資料和頁面分離,也慢慢接近了現在最流行的網頁前、後端分離的開發方式。