由於狀態零散地分佈在許多組件和組件之間的交互中,大型應用複雜度也經常逐漸增長。為瞭解決這個問題,Vue 提供 vuex。 一、什麼是Vuex Vuex 是一個專為 Vue.js 應用程式開發的 狀態管理模式 。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化 ...
由於狀態零散地分佈在許多組件和組件之間的交互中,大型應用複雜度也經常逐漸增長。為瞭解決這個問題,Vue 提供 vuex。
一、什麼是Vuex
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
狀態,其實指的是實例之間的共用數據,Vuex是管理應用中不同Vue實例之間數據共用的工具。
下圖是Vuex官方提供的對於狀態管理模式的設計理念。
二、為什麼使用Vuex
通俗的講,就是為了方便組件與組件之間的數據共用。
我們知道,父子組件之間想要共用數據,可以使用props屬性,子父組件之間的通信可以使用$emit觸發一個事件。
隨著我們的web應用不斷的豐富,組件之前通信採用的props和事件將會充斥著我們的整個項目,邏輯也會越來越複雜,程式員不得不將精力從業務邏輯的實現上轉移到組件之間的通信上。日後的維護也會不斷的複雜。
理論上,我們可以通過一個全局的共用數據來實現各個組件之間的數據共用,但是,當一個組件修改了這個全局數據時,其他組件對該全局數據的引用都會跟隨者改變,並且不留下任何痕跡,這種實現方式,會導致調試變為噩夢。
這個時候,我們迫切需要一個管理組件之間數據的共用的工具,Vuex出現了。
三、什麼情況下我該使用Vuex
雖然 Vuex 可以幫助我們管理共用狀態,但也附帶了更多的概念和框架。這需要對短期和長期效益進行權衡。
如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗餘的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的 store 模式就足夠您所需了。但是,如果您需要構建一個中大型單頁應用,您很可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成為自然而然的選擇。
四、如何使用Vuex
每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:
- Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地得到高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。
安裝
本文使用npm安裝vuex,初始化一個vuex項目目錄,然後:
`npm install vuex --save`
也可以直接下載vuex.js,然後在script標簽中引用。
開始使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script src="./node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
Vue.use(Vuex);
const myStore = new Vuex.Store({
state: {
name: "Alex",
},
mutations: {},
getters: {},
actions: {}
});
let App = {
template: `
<div>
<p>{{ name }}</p>
</div>
`,
computed: {
name: function () {
return this.$store.state.name;
},
}
};
new Vue({
el: "#app",
store: myStore,
template: `<app></app>`,
components: {
'app': App,
},
})
</script>
</body>
</html>
在以上代碼中,我們使用Vue.use(Vuex)告訴Vue實例使用Vuex進行狀態管理,並使用Vuex.Store創建了一個Vuex實例,上文提到,Store是Vuex的核心,它是一個容器,包含了幾個核心屬性。列印一下Vue實例對象,我們可以看到這幾個核心屬性,如下圖:
創建Vue實例時將新建的Vuex實例註冊到Vue實例中,即可在Vue實例中查看到$store屬性。四個核心屬性都已經創建好了。
核心概念
接下來,我們需要理解Vuex中的幾個核心概念。
State
用來存放組件之間共用的數據。他跟組件的data選項類似,只不過data選項是用來存放組件的私有數據。
Action
Actions用來處理非同步事務。
Actions 提交的是 mutations,而不是直接變更狀態。也就是說,actions會通過mutations,讓mutations幫他提交數據的變更。
Action 可以包含任意非同步操作。比如ajax、setTimeout、setInterval。繼續看下麵的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script src="./node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: 'Pizza',
age: 18,
score: 90,
},
mutations: {
changeScoreAsync: function (state, payload) {
return state.score += payload;
}
},
actions: {
addScore: function (context, payload) {
setTimeout(() => context.commit("changeScoreAsync", payload), 3000);
}
}
});
let App = {
template: `
<div>
<span>{{ name }}</span>
<span>{{ age }}</span>
<span>{{ score }}</span>
<button @click="changeScore">點擊一會兒修改數據</button>
`,
computed: {
name: function () {
return this.$store.state.name;
},
age: function () {
return this.$store.state.age;
},
score: function () {
return this.$store.state.score;
},
},
methods: {
changeScore: function () {
this.$store.dispatch("addScore", 1);
}
}
};
new Vue({
el: "#app",
template: `<App></App>`,
store,
components: {
App
}
})
</script>
</body>
</html>
點擊修改數據後,this.$store.dispatch找到actions中定義的方法,並將當前狀態的store對象封裝,傳遞給actions中函數的第一個參數context,隨後由context.commit提交修改。
Mutation
前面講到的都是如何獲取state的數據,那如何把數據存儲到state中呢?
在 Vuex store 中,實際改變狀態(state) 的唯一方式是通過提交(commit) 一個 mutation。
mutations下的函數接收state作為第一個參數,接收payload作為第二個參數,payload是用來記錄開發者使用該函數的一些信息,有一點需要註意:mutations方法必須是同步方法!
請看下麵的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script src="./node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: "Pizza",
age: 18,
score: 100,
hobby: ["girls", "books"]
},
mutations: {
score: function (state, payload) {
console.log("state: ", state);
console.log("payload: ", payload);
return state.score -= 10;
},
hobby: function (state, payload) {
return state.hobby.push(payload);
}
},
getters: {
getAge: function (state) {
return state.age + 1;
}
}
});
let App = {
template: `
<div>
<p>{{ name }}</p>
<p>{{ age }}</p>
<p>{{ score }}</p>
<p>{{ hobby }}</p>
<button @click="changeScore">點擊修改分數</button>
<button @click="changeHobby">點擊修改愛好</button>
`,
computed: {
name: function () {
return this.$store.state.name;
},
age: function () {
return this.$store.state.age;
},
score: function () {
return this.$store.state.score;
},
hobby: function () {
return this.$store.state.hobby;
}
},
methods: {
changeScore: function () {
this.$store.commit('score', 10);
},
changeHobby: function () {
this.$store.commit('hobby', 'movie')
}
}
};
new Vue({
el: "#app",
template: `<App></App>`,
components: {
App
},
store
})
</script>
</body>
</html>
我們提交了一個this.$store.commit(“score”, 10),傳入的第一個參數指的是mutations中定義的參數,第二個參數是需要變更的數據。
Getter
有時候,我們需要對state的數據進行篩選,過濾。這些操作都是在組件的計算屬性進行的。
如果多個組件需要用到篩選後的數據,那我們就必須到處重覆寫該計算屬性函數,或者將其提取到一個公共的工具函數中,並將公共函數多處導入 - 兩者都不太理想。
如果把數據篩選完在傳到計算屬性里就不用那麼麻煩了,這就是getter的作用,來看下麵的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script src="./node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: "Pizza",
age: 22,
},
getters: {
getAge: function (state) {
return state.age + 1;
}
}
});
let App = {
template: `
<div>
<span>{{ name }}</span>
<span>{{ age }}</span>
</div>
`,
computed: {
name: function () {
return this.$store.state.name;
},
age: function () {
return this.$store.getters.getAge;
}
},
};
new Vue({
el: "#app",
template: `<App></App>`,
store: store,
components: {
App
}
})
</script>
</body>
</html>
註意在getters中getAge必須使用state訪問數據,而不是this。
在計算屬性中,我們使用this.$store.getters.getAge來訪問需要計算的數據。
項目結構
Vuex 並不限制你的代碼結構。但是,它規定了一些需要遵守的規則:
- 應用層級的狀態應該集中到單個 store 對象中;
- 提交 mutation 是更改狀態的唯一方法,並且這個過程是同步的;
- 非同步邏輯都應該封裝到 action 裡面。
只要你遵守以上規則,如何組織代碼隨你便。如果你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。
對於大型應用,我們會希望把 Vuex 相關代碼分割到模塊中。下麵是項目結構示例:
以上,就是關於Vuex的使用介紹。