Lala Code

Lala 的前端大補帖,歡迎一起鑽研前端技術😊

0%

Vuex & API 資料流整合

圖片切換範例

API 抽離管理,清楚管理資料

axios create 的物件會變成 axios 的實體
axios interceptors 攔截器,可以做驗證檢查

@/api/photoRequest.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import axios from "axios";

// create的物件會變成axios的實體
const photoRequest = axios.create({
baseURL: "https://vue-lessons-api.herokuapp.com/",
});

// axios 攔截器,可以做驗證檢查
// 發出 request以前執行
photoRequest.interceptors.request.use(
(config) => {
if (true) throw { response: { data: "資料不得為空" } };
return config;
},
(error) => {
return Promise.reject(error);
}
);

// 接到 response 之前執行
photoRequest.interceptors.response.use(
(response) => {
console.log(response);
return response;
},
(error) => {
return Promise.reject(error);
}
);

export const getPhotoRequest = () => photoRequest.get("/photo/list");

api 模組入口

index.js 做各模組的分類,當成模組的入口,方便管理,components 只需引入 index.js

@/api/index.js

1
2
import { getPhotoRequest } from "./photoRequest.js";
export const apiGetPhotoRequest = getPhotoRequest;

資料管理

store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import { createStore } from "vuex";
import axios from "axios";
import { apiGetPhotoRequest } from "../api";
export default createStore({
state: {
photoArr: [],
idx: 0,
isLoad: false,
},
actions: {
// 為確保API資料拿回再進行handImgLoad(),需回傳Promise,axios本身就是一個Promise的回傳
// async / await 接近同步呼叫
async handInit({ commit }) {
try {
const res = await apiGetPhotoRequest();
commit("init", res.data);
return res.data;
} catch (error) {
console.log(error.response.data);
}
},
// 上面async等於
// handInit({ commit }) {
// const res = apiGetPhotoRequest();
// return res.then((response) => {
// commit("init", response.data);
// return response.data;
// })
// },
handLoadState({ commit }, bool) {
commit("loadState", bool);
},
handAdd({ commit }) {
commit("Add");
},
handRemove({ commit }) {
commit("Remove");
},
},
mutations: {
init(state, payload) {
state.photoArr = payload;
console.log(state.photoArr);
},
loadState(state, bool) {
state.isLoad = bool;
},
Add(state) {
state.idx++;
if (state.idx > state.photoArr.length - 1) {
state.idx = 0; // 大於總數量時返回第一張
}
},
Remove(state) {
state.idx--;
if (state.idx < 0) {
state.idx = state.photoArr.length - 1; //小於0時返回最後一張
}
},
},
getters: {
isLoad(state) {
return state.isLoad;
},
photoArr(state) {
return state.photoArr;
},
idx(state) {
return state.idx;
},
},
});

組件

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { onMounted } from "vue";
import { useStore } from "vuex";
import ImageBox from "./components/ImageBox/index.vue";
import LoadingBar from "./components/LoadingBar.vue";
export default {
components: {
LoadingBar,
ImageBox,
},
setup() {
const store = useStore();

const handImgLoad = (imgArr) => {
let i = 0;
imgArr.forEach((image) => {
const newImage = new Image();
newImage.src = image.url;
newImage.onload = () => {
i++;
store.dispatch("handLoadState", i === imgArr.length);
};
});
};

const init = () => {
store.dispatch("handInit").then((res) => {
handImgLoad(res); // res為handInit回傳的資料
});
};

onMounted(() => {
init();
});

return {};
},
};
1
2
3
4
<div id="app">
<LoadingBar />
<ImageBox />
</div>

loading 組件
components/LoadingBar.vue

1
2
3
4
5
6
7
8
9
10
11
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();

const isLoad = computed(() => store.getters.isLoad);

return { isLoad };
},
};
1
<img v-show="!isLoad" class="load" src="../assets/load.gif" alt="" />

圖片組件
components/ImageBox/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { computed } from "vue";
import { useStore } from "vuex";
import ImgShow from "./ImgShow.vue";
import ChangeBtn from "./ChangeBtn.vue";
export default {
components: {
ImgShow,
ChangeBtn,
},
setup() {
const store = useStore();

const isLoad = computed(() => store.getters.isLoad);

return { isLoad };
},
};
1
2
3
4
<div class="box" v-show="isLoad">
<ImgShow />
<ChangeBtn />
</div>

圖片切換按鈕
components/ImageBox/ChangeBtn.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();

const addImg = () => {
store.dispatch("handAdd");
};

const removeImg = () => {
store.dispatch("handRemove");
};

return { addImg, removeImg };
},
};
1
2
3
4
<div class="btnbox">
<a @click="removeImg" href="javascript:;">上一張</a>
<a @click="addImg" href="javascript:;">下一張</a>
</div>

圖片顯示組件
components/ImageBox/ImgShow.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();

const photoArr = computed(() => store.getters.photoArr);

const idx = computed(() => store.getters.idx);

return { photoArr, idx };
},
};
1
2
3
4
5
6
7
8
9
10
<div>
<img
class="img"
v-for="(item, s) in photoArr"
v-show="idx === s"
:key="item.url"
:src="item.url"
alt=""
/>
</div>

以上是我的學習筆記,希望也有幫助到你哦 😀



Hey!想學習更多前端知識嗎?

最近 Lala 開了前端課程 👉【實地掌握RWD - 12小時新手實戰班】👈
無論您是 0 基礎新手,又或是想學 RWD 的初學者,
我們將帶你從零開始,深入了解並掌握 RWD 響應式網頁設計的核心技術,快來一起看看吧 😊



🚀線上課程分享

線上課程可以加速學習的時間,省去了不少看文件的時間XD,以下是我推薦的一些課程
想學習更多關於前後端的線上課程,可以參考看看。

Hahow

Hahow 有各式各樣類型的課程,而且是無限次數觀看,對學生或上班族而言,不用擔心被時間綁住



六角學院

如果你是初學者,非常推薦六角學院哦!
剛開始轉職也是上了六角的課,非常的淺顯易懂,最重要的是,隨時還有線上的助教幫你解決問題!


Udemy

Udemy 裡的課程非常的多,品質普遍不錯,且價格都滿實惠的,CP值很高!
也是很多工程師推薦的線上課程網站。
❤️