一、vuex是什么
官方解釋是:Vuex是通過全局注入store對(duì)象飞袋,來實(shí)現(xiàn)組件間的狀態(tài)共享,是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式链患。
我的理解是巧鸭,例如你的項(xiàng)目里某一個(gè)數(shù)據(jù)在前端多個(gè)組件中都有應(yīng)用,如果一個(gè)改的話麻捻,那豈不是每個(gè)組件都需要改一次纲仍,特別是類似的數(shù)據(jù)多起來的話呀袱,操作起來想想就繁雜,于是郑叠,可以通過Vuex來實(shí)現(xiàn)組件間的狀態(tài)共享夜赵,改一個(gè),其他組件中的值的狀態(tài)自動(dòng)改變乡革。
* 那么可能有人問:直接用全局對(duì)象不久可以了寇僧?
Vuex 和單純的全局對(duì)象有以下兩點(diǎn)不同:
- Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候沸版,若 store 中的狀態(tài)發(fā)生變化嘁傀,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
- 你不能直接改變 store 中的狀態(tài)视粮。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation细办。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用蕾殴。
* 那么可能有人問:Vuex與localStorage不一樣么笑撞?
vuex 是 vue 的狀態(tài)管理器,存儲(chǔ)的數(shù)據(jù)是響應(yīng)式的钓觉,但是并不會(huì)保存起來茴肥,刷新之后就回到了初始狀態(tài),但是localStorage是保存在瀏覽器中的议谷,刷新之后還可以取出來繼續(xù)使用炉爆。而且,vuex里卧晓,我們保存的一般都是數(shù)組芬首,而localStorage保存到話只支持字符串
* 那么可能有人問:既然是存數(shù)據(jù)的,那什么時(shí)候用vuex逼裆,什么時(shí)候直接用簡(jiǎn)單的通信方式哩郁稍?
- 如果項(xiàng)目足夠簡(jiǎn)單(只是多個(gè)組件間傳遞數(shù)據(jù)),最好不要使用 Vuex胜宇。一個(gè)簡(jiǎn)單的 store 模式就足夠所需了耀怜,只使用組件間常用的通信方法即可,使用 Vuex 可能是繁瑣冗余的桐愉。
Vue組件簡(jiǎn)單常用的通信方式有以下幾種:
1财破、父向子
傳值通過props的方式;
2从诲、子向父
傳值通過events ($emit)左痢,實(shí)際上就是子組件把自己的數(shù)據(jù)發(fā)送到父組件;
3、父調(diào)用子方法
通過ref俊性;provide / inject略步。
4、兄弟之間
通信通過bus
5定页、跨級(jí)嵌套通信
可以使用bus趟薄;provide / inject等。
6典徊、 vue組件間通信六種方式(完整版)
- 如果需要構(gòu)建一個(gè)中大型單頁應(yīng)用(多級(jí)組件嵌套)杭煎,一個(gè)組件更改某個(gè)數(shù)據(jù),多個(gè)組件自動(dòng)獲取更改后的數(shù)據(jù)進(jìn)行業(yè)務(wù)邏輯處理宫峦,Vuex 將會(huì)成為自然而然的選擇岔帽。
- 由上面官方給的圖可以看出vuex由以下幾部分構(gòu)成
1. State
state是存儲(chǔ)的單一狀態(tài),是存儲(chǔ)的基本數(shù)據(jù)导绷。對(duì)象必須是純粹的對(duì)象 (含有零個(gè)或多個(gè)的 key/value 對(duì))犀勒。它作為一個(gè)“唯一數(shù)據(jù)源 (SSOT)”而存在。
2. Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation更改數(shù)據(jù)【必須是同步函數(shù)】妥曲。(使用store.commit方法更改state存儲(chǔ)的狀態(tài))
3. Action
Action 類似于 mutation贾费,不同在于:
Action 提交的是 mutation,而不是直接變更狀態(tài)檐盟。
Action 可以包含任意【異步操作】褂萧。
Action 通過store.dispatch
方法觸發(fā),Mutation使用store.commit
觸發(fā)葵萎。
4. Getter
getters是store的計(jì)算屬性导犹,可以通過store 中的 state 中派生出一些狀態(tài)(比如說過濾)。就像computed計(jì)算屬性一樣羡忘,getter返回的值會(huì)根據(jù)它的依賴被緩存起來谎痢,且只有當(dāng)它的依賴值發(fā)生改變才會(huì)被重新計(jì)算。
5. Module
當(dāng)應(yīng)用變得復(fù)雜時(shí)卷雕,store對(duì)象可能變得相當(dāng)臃腫復(fù)雜节猿。為了解決這個(gè)問題,Vuex 允許我們將 store 分割成模塊(module)漫雕。每個(gè)模塊擁有自己的 state滨嘱、mutation、action浸间、getter太雨、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)
* 所以vuex的是怎么運(yùn)行的?
- vuex的運(yùn)行流程
- 在組件內(nèi)部魁蒜,通過dispatch來分發(fā)action囊扳。
- 再通過action來第調(diào)用mutation
- 進(jìn)而觸發(fā)mutation內(nèi)部的commit來修改state
- 最后state改變煤墙,導(dǎo)致頁面重新render。
二宪拥、vuex怎么用
1.安裝
NPM
npm install vuex --save
Yarn
yarn add vuex
2.中型數(shù)據(jù)不太復(fù)雜的項(xiàng)目中(直接使用)
其實(shí)使用cli工具初始化的項(xiàng)目中如果選擇安裝vuex的話就已經(jīng)有了。如下:
在src
文件夾中新建一個(gè)store
文件夾铣减,下面新建一個(gè)名為index.js
的文件她君。如下
在index.js
中注冊(cè)vuex,并且在state中初始化
一個(gè)count
變量介紹mutations
一個(gè)tasks
數(shù)組 + 一個(gè)taskFinish
方法介紹getters
一個(gè)increment
方法介紹actions
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count:0,
tasks: [
{ id: 1, finish: true },
{ id: 2, finish: false }
]
},
mutations: {
// 更改 Vuex 的 store 中的狀態(tài)的唯一方法
countAdd (state) {
// 自定義將傳過來的參數(shù)操作操作
state.count++
}
},
getters: {
// getters是store的計(jì)算屬性葫哗, 有時(shí)候需要從 store 中的 state 中派生出一些狀態(tài)(比如說過濾)
taskFinish: state => {
return state.tasks.filter(a=> a.finish)
}
},
actions: {
// actions 提交的是 mutation缔刹,而不是直接變更狀態(tài).
increment(context) {
context.commit('countAdd')
}
},
modules: {}
});
在main.js
中導(dǎo)入store實(shí)例,我們就可以通過this.$store.state
訪問這些狀態(tài)劣针,一般把它的值注入到computed
中
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 阻止啟動(dòng)生產(chǎn)消息校镐,常用作指令 ?
Vue.config.productionTip = false;
new Vue({
router,
store, // 注冊(cè),全局使用vuex捺典,(this.$store)
render: h => h(App)
}).$mount("#app");
在后綴名為.vue
的組件中使用
-
this.$store.state.count
實(shí)現(xiàn)數(shù)據(jù)調(diào)用 -
this.$store.commit('countAdd')
實(shí)現(xiàn)通過mutations實(shí)現(xiàn)數(shù)據(jù)修改(同步方式) -
this.$store.getters.taskFinish
實(shí)現(xiàn)通過getters實(shí)現(xiàn) state 中的數(shù)據(jù)派生出一些狀態(tài)(過濾數(shù)據(jù))
<!-- 測(cè)試項(xiàng)目 -->
<template>
<div>
<h3>{{this.$store.state.count}}</h3>
<h4>{{this.$store.getters.taskFinish}}</h4>
<input type="button" value="count自增" @click="countAdd"></div>
<button @click="increment">按鈕</button>
</template>
<script>
export default {
data() {
return {};
},
components: {},
computed: {},
mounted() {},
methods: {
countAdd() {
// commit不僅可以傳state的參數(shù)鸟廓,而且可以傳額外的參數(shù),只需在mutations里定義的函數(shù)后面的參數(shù)里與這里的一致就可以
this.$store.commit('countAdd')
},
increment(){
// 效果跟countAdd()是一樣的
this.$store.dispatch('increment')
}
}
};
</script>
<style>
</style>
* 看完例子之后可能有人說了襟己,mutations和action效果不是一樣的么引谜,直接使用mutations不就行了,何必再用action做一次類似于請(qǐng)求轉(zhuǎn)發(fā)的操作呢擎浴?
答 :實(shí)際上并非如此员咽。還記得 mutation 必須同步執(zhí)行這個(gè)限制么?Action 就不受約束贮预!我們可以在 action 內(nèi)部執(zhí)行異步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions 支持同樣的載荷方式和對(duì)象方式進(jìn)行分發(fā):
// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
amount: 10
})
// 以對(duì)象形式分發(fā)
store.dispatch({
type: 'incrementAsync',
amount: 10
})
當(dāng)然這些方法只是簡(jiǎn)單的介紹理解一下vuex里面的東西贝室,深入理解還需要自己實(shí)際寫一下去探索。上面輸出的格式可以簡(jiǎn)化仿吞,例如
將this.$store.state.count
放入computed
計(jì)算屬性當(dāng)中去export default { name: 'App', computed:{ count(){ return this.$store.state.count; } } }
然后直接通過
<h3>{{count}}</h3>
調(diào)用即可
3.大型數(shù)據(jù)密集型項(xiàng)目中使用(需要將其劃分為模塊使用)
方法一: 在src
文件夾中新建一個(gè)store
文件夾滑频,下面分別新建名為index.js / actions.js / getter.js / mutations.js
的文件。
方法一例子來自于vuex最詳細(xì)完整的使用用法茫藏,讓大家知道每個(gè)文件里的內(nèi)容的格式
在index.js
中
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 導(dǎo)入相應(yīng)的模塊误趴,*相當(dāng)于引入了這個(gè)組件下所有導(dǎo)出的事例,使用*和from關(guān)鍵字來實(shí)現(xiàn)的模塊的繼承
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)
// 首先聲明一個(gè)需要全局維護(hù)的狀態(tài) state,比如 我這里舉例的resturantName
const state = {
resturantName: '飛歌餐館' // 默認(rèn)值
// id: xxx 如果還有全局狀態(tài)也可以在這里添加
// name:xxx
}
// 注冊(cè)上面引入的各大模塊
const store = new Vuex.Store({
state, // 共同維護(hù)的一個(gè)狀態(tài)务傲,state里面可以是很多個(gè)全局狀態(tài)
getters, // 獲取數(shù)據(jù)并渲染
actions, // 數(shù)據(jù)的異步操作
mutations // 處理數(shù)據(jù)的唯一途徑凉当,state的改變或賦值只能在這里
})
export default store // 導(dǎo)出store并在 main.js中引用注冊(cè)。
在actions.js
中
// 給action注冊(cè)事件處理函數(shù)售葡。當(dāng)這個(gè)函數(shù)被觸發(fā)時(shí)候看杭,將狀態(tài)提交到mutations中處理
export function modifyAName({commit}, name) { // commit 提交;name即為點(diǎn)擊后傳遞過來的參數(shù)挟伙,此時(shí)是 'A餐館'
return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
return commit ('modifyBName', name)
}
// ES6精簡(jiǎn)寫法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)
在mutations.js
中
// 提交 mutations是更改Vuex狀態(tài)的唯一合法方法
export const modifyAName = (state, name) => { // A組件點(diǎn)擊更改餐館名稱為 A餐館
state.resturantName = name // 把方法傳遞過來的參數(shù)楼雹,賦值給state中的resturantName
}
export const modifyBName = (state, name) => { // B組件點(diǎn)擊更改餐館名稱為 B餐館
state.resturantName = name
}
在getters.js
中
// 獲取最終的狀態(tài)信息
export const resturantName = state => state.resturantName
在后綴為.vue
的文件中
像上面一樣類似操作
方法二:新建一個(gè)名為modules
的文件夾,modules文件夾下面分別新建自己對(duì)應(yīng)需要的模塊。
每一個(gè)
modules
文件夾下的文件里
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
moudel.exports = moduleA
在index.js
中注冊(cè)使用的話
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './modules/cart' // 導(dǎo)入相應(yīng)的模塊贮缅,使用*和from關(guān)鍵字來實(shí)現(xiàn)的模塊的繼承
import * as ...
Vue.use(Vuex)
// 注冊(cè)上面引入的各大模塊
const store = new Vuex.Store({
modules: {
a: moduleA,
...
}
})
export default store // 導(dǎo)出store并在 main.js中引用注冊(cè)榨咐。
使用:
store.state.a.xx // -> moduleA 的狀態(tài)