【一】瞭解Vuex
【1】想象一個場景
如果你的專案裡有很多頁面(元件/檢視),頁面之間存在多級的巢狀關係,此時,這些頁面假如都需要共享一個狀態的時候,此時就會產生以下兩個問題:
- 多個檢視依賴同一個狀態
- 來自不同檢視的行為需要變更同一個狀態
【2】解決方案
對於第一個問題,假如是多級巢狀關係,你可以使用父子元件傳參進行解決,雖有些麻煩,但好在可以解決;對於兄弟元件或者關係更復雜元件之間,就很難辦了,雖然可以透過各種各樣的辦法解決,可實在很不優雅,而且等專案做大了,程式碼就會變成屎山,實在令人心煩。
對於第二個問題,你可以透過父子元件直接引用,或者透過事件來變更或者同步狀態的多份複製,這種模式很脆弱,往往使得程式碼難以維護,而且同樣會讓程式碼變成屎山。
【3】換一種思路
把各個元件都需要依賴的同一個狀態抽取出來,在全域性使用單例模式進行管理
在這種模式下,任何元件都可以直接訪問到這個狀態,當狀態發生改變時,所有元件都發生更新
【4】vuex的誕生
這就是 Vuex 背後的基本思想,借鑑了 Flux、Redux。與其他模式不同的是,Vuex 是專門為 Vue 設計的狀態管理庫,以利用 Vue.js 的細粒度資料響應機制來進行高效的狀態更新。
【5】什麼時候該用vuex
這個問題因人而異,如果你不需要開發大型的單頁應用,此時你完全沒有必要使用vuex,比如你的頁面就兩三個,使用vuex後增加的檔案比你現在的頁面還要多,那就沒這個必要了。
假如你的專案達到了中大型應用的規模,此時您很可能會考慮如何更好地在元件外部管理狀態,Vuex 將會成為自然而然的選擇。
【二】安裝Vuex
進入專案,在命令列輸入安裝指令
npm install vuex --save
然後配置 vuex,使其工作起來:在src路徑下建立store資料夾,然後建立index.js檔案,檔案內容如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// 定義一個name,以供全域性使用
name: '張三',
// 定義一個number,以供全域性使用
number: 0,
// 定義一個list,以供全域性使用
list: [
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
],
},
});
export default store;
修改main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定義一個name,以供全域性使用
name: 'hqq'
},
})
最後修改APP.vue
export default {
name: 'HomeView',
data() {
return {}
},
methods: {},
created() {
console.log('全域性', this.$store.state.name)
},
}
</script>
此時,啟動專案,開啟控制檯就會發現輸出了在store裡面定義的name值
- 官方建議我們以上操作this.$store.state.XXX最好放在計算屬性中,當然,我也建議你這麼使用,這樣可以讓你的程式碼看起來更優雅一些,就像這樣:
export default {
name: 'HomeView',
data() {
return {}
},
methods: {},
computed: {
getName() {
return this.$store.state.name
}
},
created() {
console.log('計算屬性', this.getName)
},
}
</script>
此時就可以得到在控制檯得到一樣的效果
【三】修飾器Getter
【1】場景
getter時一個讀取操作的修飾利器,
假設一個場景,
- 你已經將store的name全部都展示到頁面上了,
- 此時產品經理說把所有的name前面都加上hello
- 這是如果沒有別的辦法就只能在每個頁面上使用this.$store.state.name獲取到值之後,進行遍歷,前面追加"hello"。
這樣做很不好,原因如下
- 程式碼冗餘
- 如果下次有其他需求,要把hello改成hi,又要進行很大的修改
【2】使用
所以這時候getter就登場了,它可以從源頭解決問題,它可以把state裡的資料放到一個方法裡面,得到返回值,如果有修改需求,只需要修改這個方法的返回值即可
首先,在store物件中增加getters屬性
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定義一個name,以供全域性使用
name: 'hqq'
},
getters: {
getMessage(state) {
// 獲取修飾後的name,第一個引數為必要引數,得到的就是state物件
return `大帥哥${state.name}`
}
}
})
在元件中使用
export default {
name: 'HomeView',
methods: {},
created() {
console.log(this.$store.getters.getMessage)
},
}
【四】修改值Mutation
說到修改,大多數人會想到直接得到值然後=賦值修改
this.$store.state.XXX = XXX;
這樣做確實可以達到效果,但是不好,vuex開放了專門的介面提供給我們修改
修改store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定義一個name,以供全域性使用
name: 'hqq'
},
getters: {
getMessage(state) {
// 獲取修飾後的name,第一個引數為必要引數
return `大帥哥${state.name}`
}
},
// 改值用的
mutations: {
// 第一個引數是state物件,第二個引數放要修改的值
setName(state, name) {
state.name = name
}
},
})
修改APP.vue
export default {
name: 'HomeView',
created() {
this.$store.commit('setName', 'green')
},
}
【五】非同步操作Actions
Actions存在的意義是假設你在修改state的時候有非同步操作,vuex作者不希望你將非同步操作放在Mutations中,所以就給你設定了一個區域,讓你放非同步操作,這就是Actions
修改store/index.js
const store = new Vuex.Store({
state: {
name: '張三',
number: 0,
},
mutations: {
setNumberIsWhat(state, payload) {
state.number = payload.number;
},
},
actions: {
// 增加actions屬性
setNum(content) {
// 增加setNum方法,預設第一個引數是content,其值是複製的一份store
return new Promise(resolve => {
// 我們模擬一個非同步操作,1秒後修改number為888
setTimeout(() => {
content.commit('setNumberIsWhat', { number: 888 });
resolve();
}, 1000);
});
},
},
});
修改App.vue
async mounted() {
console.log(`舊值:${this.$store.state.number}`);
await this.$store.dispatch('setNum');
console.log(`新值:${this.$store.state.number}`);
},