vuex簡(jiǎn)介
- Vuex是一個(gè)為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式
-
- 采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并且相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化
- Vuex也集成到Vue的官方調(diào)試工具 devtools extension,提供了注入令配置的time-travel調(diào)試,狀態(tài)快照導(dǎo)入導(dǎo)出功能
- 狀態(tài)管理是什么:
-
- 可以簡(jiǎn)單的將其看成把需要的多個(gè)組件共享的變量全部存儲(chǔ)在一個(gè)對(duì)象里面
- 然后將這個(gè)對(duì)象放在頂層的Vue實(shí)例中,讓其他組件可以使用
- 那么,多個(gè)組件就可以共享這個(gè)對(duì)象中的所有變量屬性了
- 簡(jiǎn)單來(lái)說(shuō)就是多個(gè)組件需要共享一個(gè)狀態(tài)(或者是變量),那么這個(gè)狀態(tài)放在哪個(gè)組件中都不合適,那么就單獨(dú)的new一個(gè)對(duì)象,把這個(gè)對(duì)象看作是一個(gè)管家,來(lái)管理這些共享的狀態(tài)信息
- 有什么狀態(tài)時(shí)需要多個(gè)組件共享:
- 如果一個(gè)項(xiàng)目,存在多個(gè)界面共享問(wèn)題
- 比如用戶的登陸狀態(tài),用戶名稱,頭像,地理位置信息,商品收藏,購(gòu)物車中的物品等等
- 這些狀態(tài)信息,都可以放在統(tǒng)一的地方,對(duì)它進(jìn)行保存和管理,而且它們還是響應(yīng)式的
Vuex 安裝
- 命令行安裝
npm install vuex --save
- 在src文件夾下創(chuàng)建 store文件夾,在該文件夾下創(chuàng)建 index.js文件,其中寫入:
import Vue from "vue";
import Vuex from "vuex"
//1. 安裝插件
Vue.use(Vuex);//2. 創(chuàng)建vuex對(duì)象
const store = new Vuex.Store({
state: {}, mutations: {}, actions: {}, getters: {}, modules: {}})//3. 導(dǎo)出story對(duì)象
export default store;
- 在main.js文件中引入vuex對(duì)象
import Vue from 'vue'
import App from './App'
import router from './router'
//引入 vuex 的store對(duì)象
import store from "./store";
Vue.config.productionTip = falsenew Vue({ el: '#app',
router, store, render: h => h(App)})
通過(guò) mutation 修改vuex管理的狀態(tài)
- 要修改vuex管理的狀態(tài)必須通過(guò)mutation
- index.js文件
import Vue from "vue";
import Vuex from "vuex"
//1. 安裝插件
Vue.use(Vuex);//2. 創(chuàng)建vuex對(duì)象
const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
counter: 10
}, mutations: {//定義方法用于修改vuex管理的狀態(tài)
increment(state){ state.counter++; }, decrement(state){ state.counter--; } }, actions: {}, getters: {}, modules: {}})//3. 導(dǎo)出story對(duì)象
export default store;
- App.vue文件
<template>
<div id="app">
<h3>{{$store.state.counter}}</h3>
<!--操作vuex管理的狀態(tài)-->
<button @click="addition">+</button>
<button @click="subtration">-</button>
<hr>
<hello-vuex/>
</div>
</template>
<script>
import HelloVuex from "./components/HelloVuex";
export default {
name: 'App',
components:{
HelloVuex
},
methods:{
addition(){
//通過(guò)mutation修改被vuex管理的狀態(tài)
this.$store.commit('increment')
},
subtration(){
//commit()傳入的是在index.js中mutations中定義的方法名
this.$store.commit('decrement')
}
}
}
</script>
- 具體描述
- 提取出一個(gè)公共的store對(duì)象,用于保存多個(gè)組件中共享的狀態(tài)
- 將store對(duì)象放置在 new Vue對(duì)象中,這樣可以保證在所有的組件中都可以使用到
- 在其他組件中使用store對(duì)象中保存的狀態(tài)即可
- 通過(guò) this.$store.屬性 的方式來(lái)訪問(wèn)狀態(tài)
- 通過(guò) this.$store.commit('mutation方法名') 來(lái)修改狀態(tài)
- 注意事項(xiàng):
- 通過(guò)提交 mutation的方式,而非直接改變 store.state.屬性
- 這是因?yàn)閂uex可以更明確的追蹤狀態(tài)的變化,所以不要直接改變store.state.counter 的值
vuex Getters 的使用
- 有時(shí)候,需要從store 中獲取一些state 變異后的狀態(tài)
- 在組件中需要拿到 store中的數(shù)據(jù),而且這個(gè)數(shù)據(jù)是需要變化后才給到組件或者頁(yè)面的時(shí)候,這時(shí)就需要永高getters
- 抽取出的vuex的index.js配置文件
import Vue from "vue";
import Vuex from "vuex"
//1. 安裝插件
Vue.use(Vuex);
//2. 創(chuàng)建vuex對(duì)象
const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
students: [
{id: 1, name: 'bob', age: 18},
{id: 2, name: 'kobe', age: 22},
{id: 3, name: 'xyx', age: 25},
{id: 4, name: 'john', age: 17}
]
},
getters: {
//這里定義的是state中變化后的內(nèi)容
more20stu(state){
return state.students.filter(s => s.age > 20);
}
}
})
//3. 導(dǎo)出story對(duì)象
export default store;
<template>
<div id="app">
<p>getters中定義的powerConter內(nèi)容----</p>
<h3>{{$store.getters.powerConter}}</h3>
<p>通過(guò)getters 中的more20stu 獲取state中age大于20的對(duì)象---</p>
<!--通過(guò)getters拿到變化后的store中的內(nèi)容-->
{{$store.getters.more20stu}}</div>
</template>
<script>
export default {
name: 'App',
}
</script>
getters作為參數(shù)和傳遞參數(shù)
- 作為參數(shù)
- 以上面的代碼為例,如果需要拿到 {{$store.getters.more20stu}} 獲取的學(xué)生的個(gè)數(shù)
- 在getters中定義的函數(shù)還可以傳入第二個(gè)參數(shù)
import Vue from "vue";
import Vuex from "vuex"
//1. 安裝插件
Vue.use(Vuex);
//2. 創(chuàng)建vuex對(duì)象
const store = new Vuex.Store({
state: {
counter: 10,
students: [
{id: 1, name: 'bob', age: 18},
{id: 2, name: 'kobe', age: 22},
{id: 3, name: 'xyx', age: 25},
{id: 4, name: 'john', age: 17}
]
},
getters: {
more20stu(state){
return state.students.filter(s => s.age > 20);
},
//傳入的第二個(gè)參數(shù)代表就是當(dāng)前對(duì)象中的getters
more20stuLenthg(state,getters){
return getters.more20stu.length;
}
},
})
//3. 導(dǎo)出story對(duì)象
export default store;
<template>
<div id="app">
<p>通過(guò)getters 中的more20stu 獲取state中age大于20的對(duì)象---</p>
{{$store.getters.more20stu}} <p>getters 作為參數(shù)</p>
<h3>{{$store.getters.more20stuLenthg}}</h3>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
- 傳遞參數(shù)
- getters默認(rèn)是不能傳遞參數(shù)的,如果希望傳遞參數(shù),那么只能讓getters本身返回另一個(gè)函數(shù)
- 要獲取年齡大于 age的stu對(duì)象,age由外部傳遞進(jìn)來(lái)
import Vue from "vue";
import Vuex from "vuex"
Vue.use(Vuex);
const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
counter: 10,
students: [
{id: 1, name: 'bob', age: 18},
{id: 2, name: 'kobe', age: 22},
{id: 3, name: 'xyx', age: 25},
{id: 4, name: 'john', age: 17}
]
},
getters: {
//獲取年齡大于 age 的學(xué)生,這個(gè)Age不是寫死的,而且別的地方傳入進(jìn)來(lái)的
moreAgeStu(state) {
//返回一個(gè)函數(shù)
return function (age) {
return state.students.filter(s => s.age > age);
}
}
}
})
//3. 導(dǎo)出story對(duì)象
export default store;
<template>
<div id="app">
<p>getters 傳遞參數(shù)----</p>
<!--傳入一個(gè)22給getters中定義的方法,這個(gè)方法返回一個(gè)函數(shù),這個(gè)22就是函數(shù)的參數(shù)-->
<p>{{$store.getters.moreAgeStu(22)}}</p>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
mutainons 攜帶參數(shù)
- vuex的store狀態(tài)的更新唯一方式是:提交Mutations
- mutations主要包括兩部分
- 字符串的事件類型(type)
- 一個(gè)回調(diào)函數(shù)(handler) ,該回調(diào)函數(shù)的第一個(gè)參數(shù)就是state
- mutations的定義方式:
mutations: {
increment(state){ state.count++; }}
- 通過(guò)mutations更新
increment: function(){
this.$stote.commit('increment')
}
- 之前的mutations改變counter是每次 ++ 或者 --,現(xiàn)在需要每次點(diǎn)擊按鈕 + 多少 -多少由外部傳入
mutations: {
// 第二個(gè)參數(shù)是外部傳入的
incrementCount(state,count){
state.counter += count;
}
},
<template>
<div id="app">
<p>mutations 傳遞參數(shù)----</p>
<h4>{{this.$store.state.counter}}</h4>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
</div>
</template>
<script>
export default {
name: 'App'
methods:{
addCount(count){
//提交,并傳入?yún)?shù)
this.$store.commit('incrementCount',count);
}
}
}
</script>
- mutitaions傳遞參數(shù)就是在提交comiit的時(shí),commit的第二個(gè)參數(shù)就是傳遞給mutations的參數(shù)
mutitaions 傳入多個(gè)參數(shù)
- 多個(gè)參數(shù)使用一個(gè)對(duì)象傳入
addStu(){
const stu = {id: 5, name: 'alan', age:30};
this.$store.commit('addSutdent',stu);
}
mutations提交風(fēng)格
- 之前通過(guò)commit進(jìn)行提交是一種普通的方式
- Vue還提供了另外一種風(fēng)格,它是一個(gè)包含type屬性的對(duì)象,被稱為mutations負(fù)載(payload)
addCount(count){
//普通的提交風(fēng)格 //this.$store.commit('incrementCount',count);
//特殊的提交風(fēng)格 this.$store.commit({
type: 'incrementCount',
count })},
mutations: {//定義方法用于修改vuex管理的狀態(tài)
incrementCount(state,payload){
state.counter += payload.count;
}
}
mutations響應(yīng)規(guī)則
- vuex的store中的state是響應(yīng)式的,當(dāng)state中的數(shù)據(jù)發(fā)生改變時(shí),vue組件會(huì)自動(dòng)更新
- 這就要求必須遵守一些vuex對(duì)應(yīng)的規(guī)則
- 提前在store中初始化好所需的屬性
- 當(dāng)給state中的對(duì)象添加新屬性時(shí),使用下面的方式
- 方式一: 使用 Vue.set(obj, 'newProp', 123)
- 方式二: 用新對(duì)象給舊對(duì)象重新賦值
- 給state中定義好的對(duì)象添加屬性
<template>
<div id="app">
<p>store.state.info的內(nèi)容是否是響應(yīng)式的-------</p>
<button @click="updateInfo">修改 info</button>
<h2>{{$store.state.info}}</h2>
</div>
</template>
<script>
export default {
name: 'App',
methods:{
//修改info對(duì)象,并且是響應(yīng)式的
updateInfo(){
this.$store.commit('updateInfo')
}
}
}
</script>
import Vue from "vue";
import Vuex from "vuex"
Vue.use(Vuex);const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
info: { name: 'xiaoyouxin',
age: 25,
height: 1.75
} }, mutations: {//定義方法用于修改vuex管理的狀態(tài)
//通過(guò)Vue.set(obj, key, value) 響應(yīng)式的修改info的信息
updateInfo(state){ Vue.set(state.info, 'addr', '重慶');
} } })export default store;
- 給state中定義好的對(duì)象刪除屬性
import Vue from "vue";
import Vuex from "vuex"
Vue.use(Vuex);const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
info: { name: 'xiaoyouxin',
age: 25,
height: 1.75
} }, mutations: {//定義方法用于修改vuex管理的狀態(tài)
deleteInfo(state){ //刪除屬性該方式做不到響應(yīng)式
// delete state.info.name;
Vue.delete(state.info, 'name');
} } })export default store;
mutation常量類型
- 在mutation中,自定義了很多事件類型(也就是其中的方法名稱)
- 當(dāng)項(xiàng)目增大時(shí),vuex管理的狀態(tài)越來(lái)越多,需要更新狀態(tài)的情況越來(lái)越多,那么意味著mutation中的方法越來(lái)越多
- 方法過(guò)多,使用者需要花費(fèi)大量的精力去記住這些方法,甚至是多個(gè)文件之間來(lái)回切換,查看方法名稱,甚至如果不是復(fù)制的時(shí)候,可能還會(huì)出現(xiàn)寫錯(cuò)的情況
- 這時(shí)候可以在store文件夾下創(chuàng)建一個(gè)js文件用于保存常量
export const INCREMENT = 'increment';
export const INCREMENTCOUNT = 'incrementCount';
export const DECREMENT = 'decrement';
export const ADDSTUDENT = 'addSutdent';
export const UPDATEINFO = 'updateInfo';
export const DELETEINFO = 'deleteInfo';
- 然后再index.js文件中引入這些常量,并修改mutations中的方法名為 :
import Vue from "vue";
import Vuex from "vuex"// 引入這些常量import { INCREMENT,DELETEINFO,DECREMENT,ADDSTUDENT,UPDATEINFO,INCREMENTCOUNT} from './mutations-types'Vue.use(Vuex);const store = new Vuex.Store({ state: { //定義被vuex管理的各個(gè)狀態(tài) counter: 10, students: [ {id: 1, name: 'bob', age: 18},
{id: 2, name: 'kobe', age: 22},
{id: 3, name: 'xyx', age: 25},
{id: 4, name: 'john', age: 17}
],
info: { name: 'xiaoyouxin', age: 25, height: 1.75 } }, mutations: { //修改方法名為 : [常量](){} 的方式
[INCREMENT](state) {
state.counter++; }, [DECREMENT](state) {
state.counter--; }, [INCREMENTCOUNT](state,payload){
state.counter += payload.count; }, [ADDSTUDENT](state,stu){
state.students.push(stu); }, [UPDATEINFO](state){
Vue.set(state.info, 'addr', '重慶'); }, [DELETEINFO](state){
Vue.delete(state.info, 'name'); } }})export default store;
- 在組件中使用這些常量
<template>
<div id="app">
<p>mutations 傳遞參數(shù)----</p>
<h4>{{this.$store.state.counter}}</h4>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
<p>mutations 傳遞參數(shù)2----</p>
<button @click="addStu">添加學(xué)生</button>
{{$store.state.students}} <p>store.state.info的內(nèi)容是否是響應(yīng)式的-------</p>
<button @click="updateInfo">修改 info 添加屬性</button>
<h2>{{$store.state.info}}</h2>
<button @click="deleteInfo">修改 info 刪除屬性</button>
<h2>{{$store.state.info}}</h2>
</div>
</template>
<script>
import HelloVuex from "./components/HelloVuex";
//引入常量
import {
INCREMENTCOUNT, ADDSTUDENT, UPDATEINFO, DELETEINFO
} from "./store/mutations-types"
export default {
name: 'App',
methods:{
addCount(count){
this.$store.commit({
type: INCREMENTCOUNT,
count
})
},
addStu(){
const stu = {id: 5, name: 'alan', age:30};
//使用常量作為commit提交的參數(shù)
this.$store.commit(ADDSTUDENT,stu);
},
updateInfo(){
//使用常量作為commit提交的參數(shù)
this.$store.commit(UPDATEINFO)
},
deleteInfo(){
this.$store.commit(DELETEINFO);
}
}
}
</script>
<style>
</style>
Action 基本定義
- 通常情況下Vuex要求mutation中的方法必須是同步方法
- 主要原因是當(dāng)我們使用devtools時(shí),devtools可以幫助我們捕捉mytations的快照
- 但是如果是異步操作,那么devtools將不能很好的追蹤這個(gè)操作由什么時(shí)候被完成
- 如果有些情況下,確實(shí)需要在vuex中進(jìn)行一些異步操作,比如網(wǎng)絡(luò)請(qǐng)求,必然時(shí)異步的,這時(shí)候使用Action,Action類似于mutations,但是是來(lái)替代mutations進(jìn)行異步操作的
- 用法,在組件中需要異步修改state中的屬性
- $this.store.dispatch('Action中的函數(shù)名')
- Action中定義的異步函數(shù)內(nèi)部通過(guò)mutitaons修改state中的屬性
import Vue from "vue";
import Vuex from "vuex"
import {
INCREMENT,DELETEINFO,DECREMENT,ADDSTUDENT,UPDATEINFO,INCREMENTCOUNT} from './mutations-types'
Vue.use(Vuex);const store = new Vuex.Store({
state: { //定義被vuex管理的各個(gè)狀態(tài)
info: { name: 'xiaoyouxin',
age: 25,
height: 1.75
} }, mutations: { [UPDATEINFO](state){ Vue.set(state.info, 'addr', '重慶');
} }, actions: { //在actions中定義的函數(shù)中異步調(diào)用mutations中的方法
aUpdateInfo(context){ setTimeout(() => {
context.commit(UPDATEINFO) },1000)
} }})export default store;
<template>
<div id="app">
<button @click="updateInfo">修改 info 添加屬性</button>
<h2>{{$store.state.info}}</h2>
</div>
</template>
<script>
import {
INCREMENTCOUNT, ADDSTUDENT, UPDATEINFO, DELETEINFO
} from "./store/mutations-types"
export default {
name: 'App',
methods:{
updateInfo(){
//通過(guò) dispatch的方式對(duì)state中的屬性進(jìn)行異步操作
this.$store.dispatch('aUpdateInfo')
}
}
}
</script>
注意: action傳遞參數(shù)的方式根mutitaions的方式一致
modules 的使用
- vue使用單一狀態(tài)樹,那么也意味著很多狀態(tài)都會(huì)交給vuex來(lái)管理
- 當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store對(duì)象就有了能變得相當(dāng)臃腫
- 為了解決這個(gè)問(wèn)題,vuex允許將store分割成模塊,而每個(gè)模塊都擁有自己的state,mutations,actions,gettets等
- 定義在模塊中的東西使用方式和之前是一樣的,不做過(guò)多筆記
- 不過(guò)getters中定義的函數(shù)除了 state,getters之外還可呢傳入第三個(gè)參數(shù) rootState,這個(gè)參數(shù)表示之前沒有用模塊分割的哪個(gè)state,可以從這個(gè)參數(shù)中取出原本定義好的屬性
- 還有一個(gè)就是在modules中的cations中定義的函數(shù)commit的時(shí)候是針對(duì)于自己這個(gè)模塊中的mutations的
項(xiàng)目結(jié)構(gòu)
- 當(dāng)vuex管理過(guò)多內(nèi)容時(shí),好的項(xiàng)目結(jié)構(gòu)可以使代碼更加清晰
- index.html
- main.js
- api
- ... 抽取出的API請(qǐng)求
- components
- App.vue
- ...
- store
- index.js 組裝模塊并導(dǎo)出store的地方
- actions.js 根級(jí)別的 action
- mutations.js 根級(jí)別的mutation
- modules
- cart.js 購(gòu)物車模塊
- user.js 用戶模塊