0%

Vue課程筆記-VueX

什麼是VueX

在一般的Vue中,同層級的元件之間,我們可以利用eventbus來做同層級的溝通,
但是,eventbus僅止於做簡單的溝通而已。
若我們想利用全域變數來讓同層級的Vue元件做構通,就又會失去Vue雙向綁定的特性。
所以,我們將所有的同階層 或 父子層的元件都丟到VueX,由VueX來做管理,
此時,我們就可以透過VueX來做到同階層的互相溝通,還有,父子層的上下溝通,
同時,又不失去VueX雙向綁定的特性。

VueX - 重要的成員屬性

state: 儲存資料狀態
actions: 如同先前提過的methods,進行非同步與取得資料,但是,特別注意,在這一個功能中並不會改變資料的狀態
getter: 如同computed,在資料呈現之前會做一些過濾的動作,就會在這個功能中做處理。
mutations: 改變資料內容的方法,而在此功能中,不會處理一些非同步的行為喔,即像是將遠端的api取進來這種事情就不會在這邊做。

小插曲-有關如何開啟課堂上提供的課程vueX檔案

首先,輸入npm install,先將node_modules這個資料夾安裝完成,
接著,安裝完成後,就輸入npm start,此時,編譯器應該就會跳你可以開啟的網頁的網址local:8080之類的東西。
有關這個npm start的啟動Vue專案指令,你可以直接去package.json的檔案中,
看一下script部分有哪些指令,像課堂上的專案中就有以下的指令

1
2
3
4
5
6
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
}

你可以看到npm start等同於npm run dev的指令,你就選擇要輸入npm startnpm run dev都可以打開專案檔囉。
也因為課堂上的專案檔是用Vue2寫的,所以,沒有指令npm run serve是很正常的喔。

新增一個 Store 管理網站資料狀態

在我實作的專案檔中,當我在商品頁面按下加入購物車,Navbar的購物車資訊是不會跟著連動作增減的,所以,這邊握們要利用VueX來達到它們可以彼此連動的效果。

在這邊呢,我們先拿loading這個特效入手,將判斷是否啟用這個效果的判斷條件加入到VueX中。

step1,先輸入指令npm install vuex --save 來安裝VueX的套件
step2,啟用VueX
我們先到main.js中,寫入

1
2
import Vuex from 'vuex'
Vue.use(Vuex);

以此來啟用Vuex套件。

step3
我們在src資料夾中,新增一個store資料夾,並在這個資料夾中新增一個index.js的檔案,
這個檔案就是用來彙整所有VueX的行為。
接著,你就在這個store資料夾中的index.js檔案裡輸入以下的內容

1
2
3
4
5
6
import Vue from 'vue';
import VueX from 'vuex';

Vue.use(VueX);
export default new VueX.Store({
});

就是在這個js檔案中啟用VueX和Vue元件。

step4.
我們要在main.js中,引入這個store資料夾底下的index.js檔案

1
2
3
4
5
6
7
8
9
import store from './store'  // 引入store檔案

new Vue({
el: '#app',
router,
store, // 新增store這個成員屬性
components: { App },
template: '<App/>',
});

step5.
先到store資料夾中的index.js檔案中,
加入state屬性,並新增一個isLoading成員屬性,並設定它為false

1
2
3
4
5
6
---index.js---
export default new VueX.Store({
state:{
isLoading: true,
},
});

step6.
接著,你到App.vue中將原本有關isLoading成員屬性的程式碼內容給註解掉,但是,不要連HTML中的isLoading內容都註解喔,那邊的不用註解。
然後,新增一個computed,接著,在裡面新增一個isLoading屬性,並讓他吃到store變數的isLoading變數,如此,你應該就可以看到你的
網頁頁面一直有loading的圈圈再轉囉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---App.vue---
---HTML---
<Loading :active.sync="isLoading"></Loading> // 吃到isLoading的回傳值

---JavaScript---
<script>
export default {
name: 'App',
data() {
return {
cart: {
carts: [],
},
// isLoading: false, // 註解掉有關isLoading的部分
};
},
computed: {
isLoading() {
return this.$store.state.isLoading; // 新增isLoading,並讓它吃到store中的isLoading值
},
},
};
</script>

記得,看到成功的呈現結果後,要把index.js中的isLoading的值,改回false喔,不然你的頁面會一直處在轉圈圈的狀態。

step7.
接著,你到Home.vue元件檔中,先將HTML文件中的isLoading元件的部分刪掉,因為,現在這個元件要統一由App.vue外層元件來呈現。
再來,就在Home.vue中將原先使用isLoading的部分,全部改成去更改store資料夾中的index.js的isLoading的資料狀態,藉此,來決定是否要呈現Loading的效果囉。

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
---Home.vue---
---JavaScript---
<script>
export default {
name: 'Home',
data() {
return {
products: [],
searchText: '',
categories: [],
// isLoading: false, // 將原本的isLoading的內容註解掉
};
},
methods: {
getProducts() {
const vm = this;
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
// vm.isLoading = true; // 將原本的isLoading的內容註解掉
this.$store.state.isLoading = true; // 切換store資料夾中的isLoading的屬性切換
this.$http.get(url).then((response) => {
vm.products = response.data.products;
console.log('取得產品列表:', response);
vm.getUnique();
// vm.isLoading = false; // 將原本的isLoading的內容註解掉
this.$store.state.isLoading = false; // 切換store資料夾中的isLoading的屬性切換
});
}
},
created() {
this.getProducts();
},
};
</script>

藉由以上的方式就完成將isLoading交由VueX來管理,並指讓外層元件App.vue來呈現Loading的效果囉~

actions 及 mutations 改變資料狀態

在這個地方,我們會利用actionsmutations來切換isLoading的資料狀態。
就不是像上一堂課程中直接修改this.$store.state.isLoading 的內容。
step1.
所以,我們就在store資料夾中的index.js檔案中新增actionsmutations的內容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---index.js---
export default new VueX.Store({
state: {
isLoading: false,
},
actions: {
updateLoading(context, status) {
context.commit('LOADING', status);
},
},
mutations: {
LOADING(state, status) {
state.isLoading = status;
},
},
});

上面可以看到,我們在actions中,新增了一個updateLoading函式來取得isLoading資料。
並在updateLoading函式中,呼叫mutations中的LOADING函式,藉此修改state的isLoading屬性值。
從這邊我們就可以看到在actions中,我們只能接收資料內容。
mutations中,我們可以修改資料的狀態。

那在actions裡面的函式有context這個參數,在VueX的官方網站,有介紹,
它是一個參數,可以取得VueX中的許多參數值,所以,看到我們藉由它的commit成員來呼叫到LOADING函式,利用它來做actionsmutations 之間的橋樑。

step2.
接著,我們就修改Home.vue元件檔中原本this.$store.state.isLoading的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---Home.vue---
<script>
export default {
methods: {
getProducts() {
const vm = this;
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
vm.$store.dispatch('updateLoading', true); // 加入這一行來呼叫index.js中的updateLoading函式,並帶入參數true
this.$http.get(url).then((response) => {
vm.products = response.data.products;
console.log('取得產品列表:', response);
vm.getUnique();
vm.$store.dispatch('updateLoading', false);
});
},
};
</script>

上面這邊的內容,我們就可以看到我們在Home.vue中,呼叫了index.js的updateLoading函式,並帶入參數true,藉此能更改isLoading的參數值。

Vuex 的嚴謹模式

在VueX的檔案中加入strict: true這段程式碼,就可以讓你的VueX檔案呈現嚴謹模式,為的是來提醒你在VueX中哪邊有寫錯,或者更改了那些不應該更改的內容囉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default new VueX.Store({
strict: true, // 加入VueX的嚴謹模式
state: {
isLoading: false,
},
actions: {
updateLoading(context, status) {
context.commit('LOADING', status);
},
},
mutations: {
LOADING(state, status) {
state.isLoading = status;
},
},
});

使用 Actions 取得遠端資料

此課程教導將原先在Home元件中,取得購物車資料的程式碼,移到VueX中,交由VueX來執行這些取得資訊的動作。
step1.
在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
---index.js---
import Vue from 'vue';
import VueX from 'vuex';
import axios from 'axios'; // 引入axios元件,因為index.js不是Vue元件,需要直接使用axios來取用遠端資料

Vue.use(VueX);
export default new VueX.Store({
strict: true,
state: {
isLoading: false,
products: [], // 新增products陣列
categories: [], // 新增categories陣列
},
actions: {
updateLoading(context, status) {
context.commit('LOADING', status);
},
getProducts(context) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
context.commit('LOADING', true);
axios.get(url).then((response) => {
console.log('取得產品列表:', response);
context.commit('PRODUCTS', response.data.products); // 呼叫mutations中的PRODUCTS函式
context.commit('CATEGORIES', response.data.products); // 呼叫mutations中的CATEGORIES函式
context.commit('LOADING', false);
});
},
},
mutations: {
LOADING(state, status) {
state.isLoading = status;
},
PRODUCTS(state, payload) { // 將products陣列塞入從遠端取得的資料
state.products = payload;
},
CATEGORIES(state, payload) { // 將遠端取的的資料的分類儲存到categories中
const categories = new Set();
payload.forEach((item) => {
categories.add(item.category);
});
state.categories = Array.from(categories);
},
},
});

以上,我們可以看到我們在index.js檔案中,新增了products 和 categories陣列,用來儲存從遠端取得資料的內容。
那我們新增了PRODUCTS 和 CATEGORIES這兩個mutations的方法,用來儲存products 和 categories的資料內容。

step2.
接著,我們要修改Home.vue檔案,直接在這個檔案中呼叫index.js的getProducts函式,取得資料,並利用computed來即時更新頁面中的內容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---Home.vue---
<script>
export default {
name: 'Home',
data() {
return {
searchText: '',
};
},
computed: {
categories() {
return this.$store.state.categories; // 回傳sotre元件中的categories內容
},
products() {
return this.$store.state.products; // 回傳sotre元件中的products內容
},
},
methods: {
getProducts() {
this.$store.dispatch('getProducts'); // 呼叫index.js檔案中的getProducts方法,來取得遠端資料
},
},
};
</script>

以上這個步驟,我們就可以透過vuex取得遠端資料後,接著,在元件檔中呼叫vuex取得遠端資料的方法。最後,當VueX元件中的資料有變動時,我們就在元件檔中利用computed來即時更新頁面上的內容。

Payload 傳遞物件參數

這個小節是在教導將取得、刪除、加入購物車的內容通通移植到VueX中,去操作。
step1.
首先,先將取得購物車的內容移到VueX中。
在App.vue元件檔中,我們先將整個getCart的內容移到index.js的actions中。
並在index.js中,新增cart的成員屬性

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
---index.js---
state: {
isLoading: false,
products: [],
categories: [],
cart: { // 新增cart屬性
carts: [],
},
},
actions: {
getCart(context) { // 新增取得購物車內容的屬性
const vm = this;
context.commit('LOADING', true);
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/cart`;
axios.get(url).then((response) => {
if (response.data.data.carts) {
vm.cart = response.data.data;
}
context.commit('LOADING', false);
context.commit('CART', vm.cart); // 呼叫mutations中的CART函式,來將資料塞入成員屬性中
});
},
},
mutations: {
LOADING(state, status) {
state.isLoading = status;
},
CART(state, payload) { // 在mutations中新增CART方法,將資料塞入cart中
state.cart = payload;
},
}

**注意~~~~**以上這個範例,你可以看到只有單純切換布林值的話,該mutations的函式(LOADING函式)參數是用status帶入,而你是要傳一筆大資料的話,mutations中的函式(CART函式)是用payload來當參數傳入。

傳遞參數到actions中,只能傳遞一個參數

當我們要傳遞多個參數的時候,我們可以用物件將這些參數包裹起來,再將這個物件傳到actions中。
如此,這樣就可以解決只能傳遞一個參數的限制囉。
我們在index.js檔案中,加入購物車 和 刪除購物車中商品的功能
step1.

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
---index.js---
export default new VueX.Store({
actions: {
removeCart(context, id) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/cart/${id}`;
context.commit('LOADING', true);
axios.delete(url).then((response) => {
context.commit('LOADING', false);
context.dispatch('getCart');
console.log('刪除購物車項目', response);
});
},
addtoCart(context, { id, qty }) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/cart`;
context.commit('LOADING', true);
const item = {
product_id: id,
qty,
};
axios.post(url, { data: item }).then((response) => {
context.commit('LOADING', false);
context.dispatch('getCart');
console.log('加入購物車:', response);
});
},
}
})

---Home.vue---
<script>
export default {
methods: {
addtoCart(id, qty = 1) {
this.$store.dispatch('addtoCart', { id, qty });
},
},
};
</script>

---App.vue---
<script>
export default {
name: 'App',
data() {
return {
};
},
methods: {
getCart() {
this.$store.dispatch('getCart');
},
removeCart(id) {
this.$store.dispatch('removeCart', id);
},
},
computed: {
isLoading() {
return this.$store.state.isLoading;
},
cart() {
console.log('購物車', this.$store.state.cart);
return this.$store.state.cart;
},
},
created() {
this.getCart();
},
};
</script>

首先,你可以看到,我們在index.js檔案中的addtoCart函式,就是用包裹物件的方式,將多個資料傳遞進去。
另外,我們在App.vue這個最外層的元件檔,執行取得購物車 和 刪除購物車的功能,
並利用computed來回船當下購物車的資料狀態。
第三,我們在Home.vue中有新增一個addtoCart的加入購物車的函式。
整體的運作過程就是,當我們在Home.vue中將商品加入購物車,此時,在VueX中會更改state.cart的內容,
而在App.vue中,就有利用computed來連動VueX中的state的cart資料。
經過以上的這種運作模式,就實現了跨父子元件階層來存取相同資料的運作囉。

Vuex 中的 getters 及 mapGetters, mapActions

前面有說到在VueX中的getters的功用就如同computed的功能一般。
所以,這邊我們把想要呈現在畫面上的computed內容,搬到VueX的getters裡面。
Step1.
我們在index.js檔案中,加入getters的屬性。

1
2
3
4
5
6
7
8
9
10
11
---index.js---
export default new VueX.Store({
getters: {
categories(state) {
return state.categories;
},
products(state) {
return state.products;
},
},
});

接著,你把原本在Home.vue中的computed回傳categories 和 products的部分刪掉。
Step2.
那我們要怎麼將在index.js中的categories 和 products呈現在畫面中呢?
我們就在Home.vue中引入VueX的mapGetters

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
---Home.vue---
---HTML---
<!-- 左側選單 (List group) -->
<div class="list-group sticky-top">
<a class="list-group-item list-group-item-action"
href="#" @click.prevent="searchText = item"
:class="{ 'active': item === searchText}"
v-for="item in categories" :key="item"> // 吃到categories的資料
<i class="fa fa-street-view" aria-hidden="true"></i>
{{ item }}
</a>
<a href="#" class="list-group-item list-group-item-action"
@click.prevent="searchText = ''"
:class="{ 'active': searchText === ''}">
全部顯示
</a>
</div>

---JavaScript---
<script>
import { mapGetters } from 'vuex'; // 這邊是用解構的方式將VueX的mapGetters取出來

computed: {
...mapGetters(['categories', 'products']),
}
</script>

用以上的方法我們就可以利用mapGetters的方式將VueX中的categories和products這兩個資料內容取出來,並利用computed的方式,將這兩個資料呈現在Home.vue的話中。
而Home.vue的html文件部分,加入categories 和 products就可以吃到這兩個資料了。
這邊的資料存取方式,是利用了解構特性中,當資料名稱和成員屬性名稱相同的時候,
就可以只寫成員屬性名稱即可。

step3.
我們也可以利用mapActions將VueX中的某些actions裡的函式也帶進來。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---Home.vue---
---JavaScript---
<script>
import { mapGetters, mapActions } from 'vuex'; // 將mapActions也從vuex中取出來

export default {
methods: {
addtoCart(id, qty = 1) {
this.$store.dispatch('addtoCart', { id, qty });
},
...mapActions(['getProducts']), // 將getProducts取出來
},
};
</script>

以上,我們也就利用了mapActions來調用在vuex中的getProducts函式。

沒有引入要傳遞的函式才能用mapActions

以上範例你可以看到在Home.vue元件檔中得addtoCarts函式我們是沒有對它使用mapActions的,
原因是addtoCart需要傳遞參數到index.js中,所以,這類型要傳遞參數的函式我們不會對它使用mapActions喔~~

模組化資料運用

在這課堂上的課程內容,主要有兩種行為,一種是跟產品(Products)有關,另一種是跟購物車有關(Carts),所以,在這邊我們會利用Vuex的模組化管理將專案檔的內容拆分成為這兩大類。
那在專案檔中的VueX,產品和購物車的內容原本是攪和在一起的,那課堂上是只有將產品部分模組化。
step1.
這邊我們在store資料夾中,新增一個products.js檔案。
然後把原本index.js檔案中的內容貼到products.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
---product.js---
import axios from 'axios'; // 引入axios
export default {
state: {
products: [],
categories: [],
},
actions: {
getProducts(context) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
context.commit('LOADING', true);
axios.get(url).then((response) => {
console.log('取得產品列表:', response);
context.commit('PRODUCTS', response.data.products);
context.commit('CATEGORIES', response.data.products);
context.commit('LOADING', false);
});
},
},
mutations: {
PRODUCTS(state, payload) {
state.products = payload;
},
CATEGORIES(state, payload) {
const categories = new Set();
payload.forEach((item) => {
categories.add(item.category);
});
state.categories = Array.from(categories);
},
},
getters: {
categories(state) {
return state.categories;
},
products(state) {
return state.products;
},
},
}

step2.
回到store資料夾中的index.js檔案,我們把跟產品相關的內容刪掉,因為,要區分開來
然後,在index.js中,將剛剛的product.js給引入進來,並且將它加到modules的屬性中

1
2
3
4
5
6
7
8
9
10
11
12
13
---index.js---
import Vue from 'vue';
import VueX from 'vuex';
import axios from 'axios';
import productModule from './product'; // 引入product.js並將它命名為productModule變數

Vue.use(VueX);
export default new VueX.Store({
modules: {
productModule, 將該變數加入到modules中
},
});

模組中的區域變數和全域變數

先介紹一下,在模組中的state是屬於區域變數,
actions, mutations, getters 都是屬於全域變數。
而這也是為什麼我們可以直接在本專案中的Home.vue元件檔,直接使用store資料夾中的index.js模組中的actions, mutations, getters的原因,因為它們都是全域變數。
但是,這樣有一個風險,如果在同一層的資料夾中,也就是在store資料夾中,萬一有其他的模組的函式的取名跟index.js的函式一樣,這樣會造成外部元件在調用這些函式時,造成衝突。
也因為有這樣的衝突狀況,所以,我們會將模組中的變數設定成區域變數,如此一來,就不會有相衝突的問題囉。

step3.
在Home.vue取用product.js模組中的products資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---Home.vue---
---JavaScript---
<script>
import { mapGetters, mapActions } from 'vuex';

export default {
methods: {
addtoCart(id, qty = 1) {
console.log(this.$store.state.productModule.products); // 提取productModule中的products資料內容
this.$store.dispatch('addtoCart', { id, qty });
},
...mapActions(['getProducts']),
},
};
</script>

你可以看到我們取用了productModule中的products資料。

語法namespaced:true,從全域變數改成區域變數

step4.
我們想要將product.js模組中的actions, mutations, getters從原本的全域變數改成區域變數,來跟外部模組的變數做區分。
首先,在product.js中加入namespaced:true 將actions, mutations, getters都改為區域變數。

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
---product.js---
export default {
namespaced: true, // 將actions, mutations, getter都改為區域變數。
state: {
products: [],
categories: [],
},
actions: {
getProducts(context) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
context.commit('LOADING', true);
axios.get(url).then((response) => {
console.log('取得產品列表:', response);
context.commit('PRODUCTS', response.data.products);
context.commit('CATEGORIES', response.data.products);
context.commit('LOADING', false);
});
},
},
mutations: {
PRODUCTS(state, payload) {
state.products = payload;
},
CATEGORIES(state, payload) {
const categories = new Set();
payload.forEach((item) => {
categories.add(item.category);
});
state.categories = Array.from(categories);
},
},
getters: {
categories(state) {
return state.categories;
},
products(state) {
return state.products;
},
},
};

接著,你要到Home.vue元件檔中,改變引入product.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
---Home.vue---
<script>
import { mapGetters, mapActions } from 'vuex';

export default {
computed: {
filterData() {
const vm = this;
if (vm.searchText) {
return vm.products.filter((item) => {
console.log(item.category.toLowerCase());
const data = item.category.toLowerCase().includes(vm.searchText.toLowerCase());
return data;
});
}
return this.products;
},
...mapGetters('productModule', ['categories', 'products']), // 在引入內容的前面要加入這些變數所處在的模組變數名稱
},
methods: {
addtoCart(id, qty = 1) {
console.log(this.$store.state.productModule.products);
this.$store.dispatch('addtoCart', { id, qty });
},
...mapActions('productModule', ['getProducts']), // 在引入內容的前面要加入這些變數所處在的模組變數名稱
},
};
</script>

利用以上這兩個操作,就能將product.js模組中的actions, mutations, getters都改為區域變數,並在Home.vue中調用這些已經變成區域變數的內容。

step5.
那這個時候,你的網頁應該會跳出有關LOADING 這個變數的錯誤。
這個原因是,在product.js中,我們有使用LOADING 這個變數,但是,它並不知道LOADING是一個global的LOADING,
它以為那是它自己的LOADING變數,而這也就是會跳LOADING是未知變數錯誤的原因喔~
所以,我們要改變一下在product.js模組中,使用LOADING的寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---product.js---
export default {
namespaced: true,
actions: {
getProducts(context) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`;
context.commit('LOADING', true, { root: true }); // 在LOADING 後面加上了{ root: true }來讓product.js模組知道這是一個global的LOADING變數
axios.get(url).then((response) => {
console.log('取得產品列表:', response);
context.commit('PRODUCTS', response.data.products);
context.commit('CATEGORIES', response.data.products);
context.commit('LOADING', false, { root: true });  // 在LOADING 後面加上了{ root: true }來讓product.js模組知道這是一個global的LOADING變數
});
},
},
};

經由以上的修改之後,你的網頁就不會再跳出相關的錯誤囉~~

VueX小結論

以上這些操作步驟就是利用VueX來管理專案檔,並讓通用變數可以跨階層被取用的方法囉~
那回到最一開始講的,購物車資料連動的效果。
其原因來自,Home.vue這個子元件是直接去修改VueX中的cart購物車資料,而App.vue是取用VueX中的cart資料,來呈現在畫面上。
所以,由上面的操作流程可以知道,父子元件都是對VueX中的同一個成員屬性的內容去操作,那當然就會有連動的效果囉。