文章目錄
- 數據準備
- 新建項目
- 選擇模塊安裝
- vscode工具打開 刪除無用文件
- 刪除src/assets文件下的所有內容
- 刪除src/components文件下的所有內容
- 修改src/app.vue
- vscode運行項目
- 一、 概述
- 1.是什么
- 2. 使用場景
- 3.優勢
- 4 Vuex流程圖
- 5.注意:
- 二、需求: 多組件共享數據
- 創建三個組件, 目錄如下
- 源代碼如下
- Son1.vue
- Son2.vue
- App.vue
- main.js不變
- 說明
- 三、vuex 的使用 - 創建倉庫
- 1.安裝 vuex
- 2.新建 store/index.js 專門存放 vuex
- 3.創建倉庫 store/index.js
- 4 在 main.js 中導入掛載到 Vue 實例上
- 5.測試打印Vuex
- 四、核心概念 - state 狀態
- 4.1 概念
- 4.2 store中定義state屬性提供數據
- 4.3 訪問Vuex中的數據
- 4.4 組件通過$store訪問Vuex中的數據
- 4.4.1 模板中使用
- 4.4.2. 組件邏輯中使用
- 4.4.3 js文件中使用
- 4.4.4 修改src/components/Son1.vue
- 4.5 組件通過mapState獲取 state中的數據
- 4.5.1 mapState作用
- 4.5.2 修改src/components/Son2.vue
- 4.5.3 運行結果
- 五、開啟嚴格模式及Vuex的單項數據流
- 1.目標
- 2.直接在組件中修改Vuex中state的值
- Son1.vue
- 運行結果
- 3.開啟嚴格模式
- 嚴格模式有什么用
- 運行結果
- 六、mutations+mapMutations同步修改倉庫屬性的值
- 6.1 概念
- 6.2 store中定義mutations
- 6.2.1 語法
- 6.2.2 代碼:修改src/store/index.js
- 6.3 組件中使用$store.emit()調用mutations方法
- 6.3.1 修改src/components/Son1.vue
- 6.3.2 運行結果
- 6.4 組件中使用mapMutations輔助函數調用mutations
- 6.4.1 mapMutations用法
- 6.4.2 代碼實現
- 6.4.3 運行結果
- 七、actions+mapActions異步修改倉庫屬性的值
- 7.1 概念
- 7.2 store中定義actions
- 7.2.1 語法
- 7.2.2 代碼:修改src/store/index.js
- 7.3 組件中使用$store.dispatch()調用actions的方法
- 7.3.1 語法
- 7.3.2 修改src/components/Son1.vue
- 7.3.3 運行結果
- 7.4 組件中使用mapActions調用actions的方法
- 7.4.1 mapActions語法
- 7.4.2 代碼實現
- 7.4.3 運行結果
- 八、核心概念-getters
- 8.1 概念getters
- 8.2 store中定義getters
- 8.2.1 語法
- 8.2.2 代碼:修改src/store/index.js
- 8.3 組件中使用$store.commit調用getters的方法
- 8.3.1 語法
- 8.3.2 修改src/components/Son1.vue
- 8.3.3 運行結果
- 8.4 組件中使用mapGetters調用getters的方法
- 8.4.1 mapGetters語法
- 8.4.2 代碼實現
- 8.4.3 運行結果
- 9 前面四個屬性的總結
- 十、核心概念 - 模塊 module
- 10.1 概念
- 10.2 模塊定義
- 10.2.1 新建src/store/modules/user.js
- 10.2.2 新建src/store/modules/setting.js
- 10.2.3 修改src/store/index.js
- 10.2.4 修改src/components/Son1.vue
- 10.2.5 修改src/components/Son2.vue
- 10.2.6 運行結果如下
- 10.3 命名空間
- 10.3.1 什么是命名空間
- 10.3.2 如何在子模塊中開啟命名空間
- 10.4 分模塊的state使用和獲取
- 10.5 分模塊的mutations使用和獲取
- 10.6 分模塊的actions使用和獲取
- 10.7 分模塊的getters使用和獲取
- 10.8 代碼演示
- 修改src/components/Son1.vue
- 修改src/components/Son2.vue
- 總結
- 流程圖
- 不使用分模塊的寫法
- 使用分模塊寫法
- 開啟嚴格模式
- 開啟命名空間
數據準備
新建項目
- 檢查版本
node
版本:20.18.0
@vue/cli
版本:5.0.8
- 新建文件夾,輸入cmd回車打開命令行窗口
- 命令行窗口:輸入指令
vue create 項目名
選擇模塊安裝
vscode工具打開 刪除無用文件
刪除src/assets文件下的所有內容
刪除src/components文件下的所有內容
修改src/app.vue
<template><div id="app"></div>
</template><script>export default {name: 'App'
}
</script><style lang="scss">
</style>
vscode運行項目
快捷鍵打開終端Ctrl + Shift + 反引號
打開終端。反引號是tab鍵上面那個
運行如下指令啟動項目
npm run serve
一、 概述
1.是什么
Vuex 是一個 Vue 的 狀態管理工具,狀態就是數據。
官網如下:https://vuex.vuejs.org/zh/
大白話:Vuex 是一個插件,可以幫我們管理 Vue 通用的數據 (多組件共享的數據)。例如:購物車數據 個人信息數
2. 使用場景
- 某個狀態 在 很多個組件 來使用 (個人信息)
主頁需要展示 XXX訂單需要寫你的名字等等。 - 多個組件 共同維護 一份數據 (購物車)
3.優勢
- 共同維護一份數據,數據集中化管理
- 響應式變化
- 操作簡潔 (vuex提供了一些輔助函數)
4 Vuex流程圖
這個圖等講完后就能看懂了。
5.注意:
官方原文:
- 不是所有的場景都適用于vuex,只有在必要的時候才使用vuex
- 使用了vuex之后,會附加更多的框架中的概念進來,增加了項目的復雜度 (數據的操作更便捷,數據的流動更清晰)
Vuex就像《近視眼鏡》, 你自然會知道什么時候需要用它~
二、需求: 多組件共享數據
目標:基于腳手架創建項目,構建 vuex 多組件數據共享環境
效果是三個組件共享一份數據:
- 任意一個組件都可以修改數據
- 三個組件的數據是同步的
創建三個組件, 目錄如下
|-components
|--Son1.vue
|--Son2.vue
|-App.vue
源代碼如下
Son1.vue
<template><div class="box"><h2>Son1 子組件</h2>從vuex中獲取的值: <label></label><br><button>值 + 1</button></div>
</template><script>
export default {name: 'Son1Com'
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
Son2.vue
<template><div class="box"><h2>Son2 子組件</h2>從vuex中獲取的值:<label></label><br /><button>值 - 1</button></div>
</template><script>
export default {name: 'Son2Com'
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
App.vue
App.vue
在入口組件中引入 Son1 和 Son2 這兩個子組件
<template><div id="app"><h1>根組件</h1><input type="text"><Son1></Son1><hr><Son2></Son2></div>
</template><script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'export default {name: 'app',data: function () {return {}},components: {Son1,Son2}
}
</script><style>
#app {width: 600px;margin: 20px auto;border: 3px solid #ccc;border-radius: 3px;padding: 10px;
}
</style>
main.js不變
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App)
}).$mount('#app')
說明
上圖中的Son1使用原始方法獲取值Vuex的值 進行演示講解
Son2使用輔助函數的方式獲取值Vuex的值 進行演示講解
三、vuex 的使用 - 創建倉庫
1.安裝 vuex
安裝vuex與vue-router類似,vuex是一個獨立存在的插件,如果腳手架初始化沒有選 vuex,就需要額外安裝。
yarn add vuex@3 或者 npm i vuex@3
注意
- vue2 安裝Vuex3.0 版本 Router 3.0 版本
- vue3 安裝Vuex4.0 版本 Router 4.0 版本
2.新建 store/index.js 專門存放 vuex
為了維護項目目錄的整潔,在src目錄下新建一個store目錄其下放置一個index.js文件。 (和 router/index.js
類似)
3.創建倉庫 store/index.js
// 導入 vue
import Vue from 'vue'
// 導入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 進行插件的安裝初始化
Vue.use(Vuex)// 創建倉庫 store
const store = new Vuex.Store()// 導出倉庫
export default store
4 在 main.js 中導入掛載到 Vue 實例上
import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({render: h => h(App),store
}).$mount('#app')
此刻起, 就成功創建了一個 空倉庫!!
5.測試打印Vuex
App.vue
created(){console.log(this.$store)
}
四、核心概念 - state 狀態
4.1 概念
State提供唯一的公共數據源,所有共享的數據都要統一放到Store中的State中存儲。即上圖的State
—>Vue Components
4.2 store中定義state屬性提供數據
打開項目中的store.js文件,在state對象中可以添加我們要共享的數據。
修改src/store/index.js
// 導入 vue
import Vue from 'vue'
// 導入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 進行插件的安裝初始化
Vue.use(Vuex)// 創建倉庫 store
const store = new Vuex.Store({// state 狀態, 即數據, 類似于vue組件中的data,// 區別:// 1.data 是組件自己的數據, // 2.state 中的數據整個vue項目的組件都能訪問到state: {count: 101}
})
// 導出倉庫
export default store
4.3 訪問Vuex中的數據
問題: 如何在組件中獲取count?
- 通過$store直接訪問 —> {{ $store.state.count }}
- 通過輔助函數mapState 映射計算屬性 —> {{ count }}(后面講)
4.4 組件通過$store訪問Vuex中的數據
獲取 store:1.Vue模板中獲取 this.$store2.js文件中獲取 import 導入 store模板中: {{ $store.state.xxx }}
組件邏輯中: this.$store.state.xxx
JS模塊中: store.state.xxx
4.4.1 模板中使用
組件中可以使用 $store 獲取到vuex中的store對象實例,可通過state屬性屬性獲取count, 如下
<h1>state的數據 - {{ $store.state.count }}</h1>
4.4.2. 組件邏輯中使用
將state屬性定義在計算屬性中 https://vuex.vuejs.org/zh/guide/state.html
<h1>state的數據 - {{ count }}</h1>
// 把state中數據,定義在組件內的計算屬性中computed: {count () {return this.$store.state.count}}
4.4.3 js文件中使用
//main.js
import store from "@/store"
console.log(store.state.count)
每次都像這樣一個個的提供計算屬性, 太麻煩了,我們有沒有簡單的語法幫我們獲取state中的值呢?
4.4.4 修改src/components/Son1.vue
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2><!-- 模板中使用 --><h4>從vuex中獲取count的值:{{$store.state.count}}</h4><!-- 組件邏輯中使用 --><h4>從vuex中獲取title的值:{{title}}</h4><br><button @click="handleAdd">值 + 1</button></div>
</template><script>
export default {name: 'Son1Com',computed: {title() {return this.$store.state.title}},methods: {handleAdd() {}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
4.5 組件通過mapState獲取 state中的數據
4.5.1 mapState作用
mapState是輔助函數,幫助我們把store中的數據
自動
映射到 組件的計算屬性中, 它屬于一種方便的用法
4.5.2 修改src/components/Son2.vue
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2><h4>從vuex中獲取count的值:{{count}}</h4><h4>從vuex中獲取title的值:{{title}}</h4><br /><button>值 - 1</button></div>
</template><script>
// 引入 mapState 輔助函數
import { mapState } from "vuex";
export default {name: 'Son2Com',computed: {...mapState(["count", "title"]),// 等價于// count () {// return this.$store.state.count;// }// title () {// return this.$store.state.title;// }},
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
4.5.3 運行結果
五、開啟嚴格模式及Vuex的單項數據流
1.目標
明確 vuex 同樣遵循單向數據流,組件中不能直接修改倉庫的數據
2.直接在組件中修改Vuex中state的值
Son1.vue
<template><div class="box"><h2>Son1 子組件</h2>從vuex中獲取的值: <label></label><br><!-- 1.綁定點擊事件 --><button @click="handleAdd">值 + 1</button></div>
</template><script>
export default {name: 'Son1Com',// 定義方法methods: {handleAdd() {this.$store.state.count++}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
運行結果
不會報錯,這是因為沒有開啟嚴格模式
3.開啟嚴格模式
嚴格模式有什么用
通過 strict: true
可以開啟嚴格模式,開啟嚴格模式后,直接修改state中的值會報錯
state數據的修改只能通過mutations,并且mutations必須是同步的
運行結果
六、mutations+mapMutations同步修改倉庫屬性的值
6.1 概念
上面說了state中的屬性值 不能直接修改
就需要使用store的mutations屬性定義方法去修改state中的值。
6.2 store中定義mutations
6.2.1 語法
注意事項
- mutations 修改state中的數據, 類似于vue組件中的methods
- mutations中屬性的方法
+第一個參數
是當前store的state屬性
+第二個參數
是payload(傳遞)載荷 用于傳遞參數
+ 注意:payload(傳遞)載荷只能有一個,想要傳遞多個參數要使用對象或者數組的形式。- mutations中的方法, 必須是同步方法。同步方法是指js中的普通方法。異步方法下面會講。
6.2.2 代碼:修改src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 101,title: 'hello world'},// mutations 修改state中的數據, 類似于vue組件中的methodsmutations: {// mutations中的方法, // 第一個參數是當前store的state屬性// 第二個參數是payload(傳遞)載荷 用于傳遞參數// 注意:payload(傳遞)載荷只能有一個,想要傳遞多個參數要使用對象或者數組的形式// mutations中的方法, 必須是同步方法// 傳單個參數addCount(state, num) {state.count += num},// 傳對象subCount(state, obj) {state.count -= obj.numconsole.log(obj.name);}},})export default store
6.3 組件中使用$store.emit()調用mutations方法
6.3.1 修改src/components/Son1.vue
由于上面在main.js全局已經全局掛在了所以不需要導入。
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2><!-- 模板中使用 --><h4>從vuex中獲取count的值:{{$store.state.count}}</h4><!-- 組件邏輯中使用 --><h4>從vuex中獲取title的值:{{title}}</h4><br><button @click="handleAdd">值 + 1</button><button @click="$store.commit('addCount', 2)">值 + 2</button><button @click="handleSub">值 - 1</button></div>
</template><script>
export default {name: 'Son1Com',computed: {title() {return this.$store.state.title}},methods: {handleAdd() {this.$store.commit('addCount', 1)},handleSub() {this.$store.commit('subCount', {num: 1, name: '張三'})}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
6.3.2 運行結果
6.4 組件中使用mapMutations輔助函數調用mutations
6.4.1 mapMutations用法
<template><button @click="addCount(1)">值 + 1</button><button @click="subCount({num: 1, name: 'hello'})">值 - 1</button>
</template>
<script>
import { mapMutations} from "vuex";
export default {// ...methods: {...mapMutations(["addCount","subCount"])},
}
</script>
6.4.2 代碼實現
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2><h4>從vuex中獲取count的值:{{count}}</h4><h4>從vuex中獲取title的值:{{title}}</h4><br /><button @click="addCount(1)">值 + 1</button><button @click="subCount({num: 1, name: 'hello'})">值 - 1</button></div>
</template><script>
// 引入 mapState 輔助函數
import { mapState,mapMutations} from "vuex";
export default {name: 'Son2Com',methods: {...mapMutations(["addCount","subCount"])},computed: {...mapState(["count", "title"]),// 等價于// count () {// return this.$store.state.count;// }// title () {// return this.$store.state.title;// }},
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
6.4.3 運行結果
七、actions+mapActions異步修改倉庫屬性的值
7.1 概念
上面說了使用store的mutations屬性定義方法去修改state中的值mutations中的方法,。這個必須是同步方法。
什么是同步方法?:普通方法。
什么是異步方法?:異步方法是指那些不會阻塞主線程執行、允許程序在等待操作完成期間繼續執行其他任務的函數。
異步方法有哪些
- 回調函數(Callbacks):?將函數作為參數傳遞給另一個函數,在異步操作完成后被調用。這是最基礎的異步模式,但嵌套過多易導致“回調地獄”
setTimeout(() => {console.log("異步操作完成"); }, 1000);
- Promise:ES6引入的異步處理對象,表示一個異步操作的最終狀態(pending/fulfilled/rejected)。通過鏈式調用(.then()/.catch())管理依賴關系,解決回調嵌套問題 。
const promise = new Promise((resolve, reject) => {setTimeout(() => resolve("成功"), 1000); }); promise.then(result => console.log(result));
- ?Async/Await?:ES8基于Promise的語法糖。async標記的函數返回Promise,await暫停函數執行直至Promise完成,使異步代碼寫法類似同步
async function fetchData() {try {const data = await fetch("/api/data");return data.json();} catch (error) {console.error(error);} }
作用:mapActions 是把位于 actions中的方法提取了出來,映射到組件methods中。
mutations是同步更新數據 (便于監測數據的變化, 更新視圖等, 方便于調試工具查看變化)。
actions則負責進行異步操作。里面的方法是異步的。
流程如下圖所示。異步修改和同步修改的流程如下。
7.2 store中定義actions
7.2.1 語法
// ....
const store = new Vuex.Store({// ...mutations: {addCount(state, num) {state.count += num}},actions: {addCountAsync(context, num) {// 1秒后調用mutations中的addCount方法setTimeout(() => {context.commit('addCount', num)}, 1000)}}// ...
})
- actions中的方法,
- 第一個參數是context 上下文屬性,
- 包含了 state(上面的state),
commit(用于調用mutations中的方法),
dispatch(用于調用actions中的方法),
getters(后面講),
rootState, rootGetters- 第二個參數是payload(傳遞)載荷 用于傳遞參數
- 注意:payload(傳遞)載荷只能有一個,想要傳遞多個參數要使用對象或者數組的形式
- actions中的方法, 寫異步方法
7.2.2 代碼:修改src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 101,title: 'hello world'},// mutations 修改state中的數據, 類似于vue組件中的methodsmutations: {// mutations中的方法, // 第一個參數是當前store的state屬性// 第二個參數是payload(傳遞)載荷 用于傳遞參數// 注意:payload(傳遞)載荷只能有一個,想要傳遞多個參數要使用對象或者數組的形式// mutations中的方法, 必須是同步方法// 傳單個參數addCount(state, num) {state.count += num},// 傳對象subCount(state, obj) {state.count -= obj.numconsole.log(obj.name);}},// actions 類似于mutations, 但是actions中可以寫異步方法actions: {// actions中的方法, // 第一個參數是context 上下文屬性, // 包含了 state(上面的state), // commit(用于調用mutations中的方法), // dispatch(用于調用actions中的方法),// getters(后面講), // rootState, rootGetters// 第二個參數是payload(傳遞)載荷 用于傳遞參數// 注意:payload(傳遞)載荷只能有一個,想要傳遞多個參數要使用對象或者數組的形式// actions中的方法, 寫異步方法addCountAsync(context, num) {// console.log(context);// 1秒后調用mutations中的addCount方法setTimeout(() => {context.commit('addCount', num)}, 1000)},subCountAsync(context, obj) {// 1秒后調用mutations中的subCount方法setTimeout(() => {context.commit('subCount', obj);}, 1000)}}})
export default store
7.3 組件中使用$store.dispatch()調用actions的方法
7.3.1 語法
<template><div><!-- $store.dispatch調用actions中的方法 --><button @click="$store.dispatch('addCountAsync', 2)">1秒后值 + 2</button><button @click="handleAddAsync()">1秒后值 - 2</button></div>
</template><script>
export default {methods: {handleAddAsync() {this.$store.dispatch('subCountAsync', {num: 2, name: '李四'})}}
}
</script>
7.3.2 修改src/components/Son1.vue
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2><!-- 模板中使用 --><h4>從vuex中獲取count的值:{{$store.state.count}}</h4><!-- 組件邏輯中使用 --><h4>從vuex中獲取title的值:{{title}}</h4><br><button @click="handleAdd">值 + 1</button><button @click="$store.commit('addCount', 2)">值 + 2</button><button @click="handleSub">值 - 1</button><!-- $store.dispatch調用actions中的方法 --><button @click="$store.dispatch('addCountAsync', 2)">1秒后值 + 2</button><button @click="handleAddAsync()">1秒后值 - 2</button></div>
</template><script>
export default {name: 'Son1Com',computed: {title() {return this.$store.state.title}},methods: {handleAdd() {this.$store.commit('addCount', 1)},handleSub() {this.$store.commit('subCount', {num: 1, name: '張三'})},handleAddAsync() {this.$store.dispatch('subCountAsync', {num: 2, name: '李四'})}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
7.3.3 運行結果
7.4 組件中使用mapActions調用actions的方法
7.4.1 mapActions語法
<template><div></div>
</template><script>
export default {}
</script>
7.4.2 代碼實現
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2><h4>從vuex中獲取count的值:{{count}}</h4><h4>從vuex中獲取title的值:{{title}}</h4><br /><button @click="addCount(1)">值 + 1</button><button @click="subCount({num: 1, name: 'hello'})">值 - 1</button><!-- mapActions輔助函數調用actions中的方法 --><button @click="addCountAsync(1)">一秒后值 + 1</button><button @click="subCountAsync({num: 1, name: 'hello'})">一秒后值 - 1</button></div>
</template><script>
// 引入 mapState 輔助函數
import { mapState,mapMutations, mapActions} from "vuex";
export default {name: 'Son2Com',methods: {...mapMutations(["addCount","subCount"]),...mapActions(["addCountAsync","subCountAsync"]),// 等價于// addCountAsync () {// this.$store.dispatch("addCountAsync", 1);// }// subCountAsync () {// this.$store.dispatch("subCountAsync",{num: 1, name: 'hello'});// }},computed: {...mapState(["count", "title"]),},
}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
7.4.3 運行結果
八、核心概念-getters
8.1 概念getters
除了state之外,有時我們還需要從state中派生出一些狀態
,這些狀態是依賴state的
,此時會用到getters。
應用1:state中定義了list,為 1-10 的數組,組件中,需要顯示所有大于5的數據。當state中的數據修改后,會影響getters中的屬性。
應用2:計算購物車的總價格。
8.2 store中定義getters
8.2.1 語法
// ....
const store = new Vuex.Store({// ...state: {items: [ // 商品數組{ id: 1, name: "iPhone", price: 5000, quantity: 2 },{ id: 2, name: "MacBook", price: 12000, quantity: 1 }]},mutations: {// 添加商品addItem(state, item) {state.items.push(item)},// 刪除商品removeItem(state, id) {state.items = state.items.filter(item => item.id !== id)}},// getters 用來獲取state中的數據, 類似于vue組件中的computedgetters: {// getters中的方法, // 第一個參數是state屬性// 第二個參數是getters屬性// getters中的方法, 必須是同步方法// 計算items數組中所有商品的總價格totalPrice: (state) => {return state.items.reduce((total, item) => {return total + item.price * item.quantity;}, 0).toFixed(2); // 保留兩位小數},// 計算items數組中所有商品的平均價格averagePrice: (state, getters) => getters.totalPrice / state.items.length}})// ...
8.2.2 代碼:修改src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 101,title: 'hello world',items: [ // 商品數組{ id: 1, name: "iPhone", price: 5000, quantity: 2 },{ id: 2, name: "MacBook", price: 12000, quantity: 1 }]},mutations: {addCount(state, num) {state.count += num},subCount(state, obj) {state.count -= obj.numconsole.log(obj.name);},// 添加商品addItem(state, item) {state.items.push(item)},// 刪除商品removeItem(state, id) {state.items = state.items.filter(item => item.id !== id)}},actions: {addCountAsync(context, num) {setTimeout(() => {context.commit('addCount', num)}, 1000)},subCountAsync(context, obj) {setTimeout(() => {context.commit('subCount', obj);}, 1000)},},// getters 用來獲取state中的數據, 類似于vue組件中的computedgetters: {// getters中的方法, // 第一個參數是state屬性// 第二個參數是getters屬性// getters中的方法, 必須是同步方法// 計算items數組中所有商品的總價格totalPrice: (state) => {return state.items.reduce((total, item) => {return total + item.price * item.quantity;}, 0).toFixed(2); // 保留兩位小數},// 計算items數組中所有商品的平均價格averagePrice: (state, getters) => getters.totalPrice / state.items.length}})export default store
8.3 組件中使用$store.commit調用getters的方法
8.3.1 語法
<template><div class="box"><!-- 在js中要加this進行調用,即this.$store.getters.totalPrice --><h4>從vuex中獲取items的值:{{$store.state.items}}</h4><button @click="$store.commit('addItem', {id: 3, name: 'AirPods', price: 1000, quantity: 1})">添加商品</button><button @click="$store.commit('removeItem', 1)">刪除商品</button><br><h4>從vuex中獲取items的總價格:{{$store.getters.totalPrice}}</h4><h4>從vuex中獲取items的平均價格:{{$store.getters.averagePrice}}</h4></div>
</template>
8.3.2 修改src/components/Son1.vue
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2><!-- 模板中使用 --><h4>從vuex中獲取count的值:{{$store.state.count}}</h4><!-- 組件邏輯中使用 --><h4>從vuex中獲取title的值:{{title}}</h4><br><button @click="handleAdd">值 + 1</button><button @click="$store.commit('addCount', 2)">值 + 2</button><button @click="handleSub">值 - 1</button><!-- $store.dispatch調用actions中的方法 --><button @click="$store.dispatch('addCountAsync', 2)">1秒后值 + 2</button><button @click="handleAddAsync()">1秒后值 - 2</button><br><h4>從vuex中獲取items的值:{{$store.state.items}}</h4><button @click="$store.commit('addItem', {id: 3, name: 'AirPods', price: 1000, quantity: 1})">添加商品</button><button @click="$store.commit('removeItem', 1)">刪除商品</button><br><h4>從vuex中獲取items的總價格:{{$store.getters.totalPrice}}</h4><h4>從vuex中獲取items的平均價格:{{$store.getters.averagePrice}}</h4></div>
</template><script>
export default {name: 'Son1Com',computed: {title() {return this.$store.state.title}},methods: {handleAdd() {this.$store.commit('addCount', 1)},handleSub() {this.$store.commit('subCount', {num: 1, name: '張三'})},handleAddAsync() {this.$store.dispatch('subCountAsync', {num: 2, name: '李四'})}}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
8.3.3 運行結果
8.4 組件中使用mapGetters調用getters的方法
8.4.1 mapGetters語法
<template><div class="box"><h4>{{items}}</h4><!-- 這里參數太長 我創建addhandle添加商品 --><button @click="addhandle()">添加商品</button><button @click="removeItem(1)">刪除商品</button><br /><h4>從vuex中獲取items數組中所有商品的總價格:{{totalPrice}}</h4><h4>從vuex中獲取items數組中所有商品的平均價格:{{averagePrice}}</h4></div>
</template><script>
// 引入 mapState 輔助函數
import { mapState,mapMutations, mapActions, mapGetters} from "vuex";
export default {name: 'Son2Com',computed: {// 將items數組中的數據映射到當前組件中...mapState(["items"]),// getters 用來獲取state中的數據, 類似于vue組件中的computed...mapGetters(["totalPrice", "averagePrice"]),},methods: {// 將 添加商品 和 刪除商品 引入。...mapMutations(["addItem","removeItem"]),// 添加商品addhandle() {this.addItem({id: 3, name: 'AirPods', price: 1000, quantity: 1});}}}
</script>
8.4.2 代碼實現
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2><h4>從vuex中獲取count的值:{{count}}</h4><h4>從vuex中獲取title的值:{{title}}</h4><br /><button @click="addCount(1)">值 + 1</button><button @click="subCount({num: 1, name: 'hello'})">值 - 1</button><!-- mapActions輔助函數調用actions中的方法 --><button @click="addCountAsync(1)">一秒后值 + 1</button><button @click="subCountAsync({num: 1, name: 'hello'})">一秒后值 - 1</button><br /><h4>{{items}}</h4><!-- 這里參數太長 我創建addhandle添加商品 --><button @click="addhandle()">添加商品</button><button @click="removeItem(1)">刪除商品</button><br /><h4>從vuex中獲取items數組中所有商品的總價格:{{totalPrice}}</h4><h4>從vuex中獲取items數組中所有商品的平均價格:{{averagePrice}}</h4></div>
</template><script>
// 引入 mapState 輔助函數
import { mapState,mapMutations, mapActions, mapGetters} from "vuex";
export default {name: 'Son2Com',computed: {// 將items數組中的數據映射到當前組件中...mapState(["count", "title", "items"]),// getters 用來獲取state中的數據, 類似于vue組件中的computed...mapGetters(["totalPrice", "averagePrice"]),},methods: {// 將 添加商品 和 刪除商品 引入。...mapMutations(["addCount","subCount","addItem","removeItem"]),...mapActions(["addCountAsync","subCountAsync"]),// 添加商品addhandle() {this.addItem({id: 3, name: 'AirPods', price: 1000, quantity: 1});}}}
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
8.4.3 運行結果
9 前面四個屬性的總結
十、核心概念 - 模塊 module
10.1 概念
由于使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常復雜時,store 對象就有可能變得相當臃腫。
這句話的意思是,如果把所有的狀態都放在state中,當項目變得越來越大的時候,Vuex會變得越來越難以維護
由此,又有了Vuex的模塊化
10.2 模塊定義
定義兩個模塊 user 和 setting。這兩個模塊分別是用戶信息和設置信息。
10.2.1 新建src/store/modules/user.js
user中管理用戶的信息狀態
const state = {userInfo: {name: 'zs',age: 18}
}const mutations = {}const actions = {}const getters = {}export default {state,mutations,actions,getters
}
10.2.2 新建src/store/modules/setting.js
setting中管理項目應用的 主題色 theme,描述 desc
const state = {theme: 'dark',desc: '描述真呀真不錯'
}const mutations = {}const actions = {}const getters = {}export default {state,mutations,actions,getters
}
10.2.3 修改src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import setting from './modules/setting'
Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 101,},mutations: {},actions: {},getters: {},modules: {user,setting}})export default store
10.2.4 修改src/components/Son1.vue
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2></div>
</template><script>
export default {computed: {},methods: {}
}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
10.2.5 修改src/components/Son2.vue
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2></div>
</template><script>
export default {computed: {},methods: {},
};
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
10.2.6 運行結果如下
10.3 命名空間
10.3.1 什么是命名空間
我們知道上面雖然都分模塊了,但是他們都還是會掛到根級別的state、getters、mutations、actions下。
如果我的user模塊和setting模塊下的state下都有一個叫做aa的屬性。此時我使用輔助函數調用這個屬性mapState(['aa'])
,代碼無法識別這個aa是user模塊下的aa屬性 還是setting模塊下的aa屬性。
開啟命名空間
解決不同模塊的state、getters、mutations、actions重名問題
一般命名空間都是開啟的
10.3.2 如何在子模塊中開啟命名空間
const state = {userInfo: {name: 'zs',age: 18}
}const mutations = {}const actions = {}const getters = {}export default {// 開啟命名空間// 1. 解決不同模塊命名沖突// 2. 解決不同模塊的state、getters、mutations、actions重名問題namespaced: true,state,mutations,actions,getters
}
10.4 分模塊的state使用和獲取
盡管已經分模塊了,但其實子模塊的狀態,還是會掛到根級別的 state 中,屬性名就是模塊名使用模塊中的數據
① 直接通過模塊名訪問 $store.state.模塊名.xxx
② 通過 mapState 映射
默認根級別的映射 mapState([ 'xxx' ])
子模塊的映射 mapState('模塊名', ['xxx','xxx'...])
- 需要開啟命名空間
10.5 分模塊的mutations使用和獲取
注意:默認模塊中的 mutation 和 actions 會被掛載到全局,需要開啟命名空間,才會掛載到子模塊。
調用子模塊中 mutation:
① 直接通過 store 調用 $store.commit('模塊名/xxx', 額外參數)
② 通過 mapMutations 映射
默認根級別的映射 mapMutations([ 'xxx' ])
子模塊的映射 mapMutations('模塊名', ['xxx','xxx'...])
- 需要開啟命名空間
10.6 分模塊的actions使用和獲取
注意:默認模塊中的 mutation 和 actions 會被掛載到全局,需要開啟命名空間,才會掛載到子模塊。
調用子模塊中 action :
① 直接通過 store 調用 $store.dispatch('模塊名/xxx ', 額外參數)
② 通過 mapActions 映射
默認根級別的映射 mapActions([ ‘xxx’ ])
子模塊的映射 mapActions(‘模塊名’, [‘xxx’,‘xxx’]) - 需要開啟命名空間
10.7 分模塊的getters使用和獲取
使用模塊中 getters 中的數據:
① 直接通過模塊名訪問 $store.getters['模塊名/xxx']
注意:沒使用模塊之前是使用的 $store.getters.xxx。
注意:使用模塊后不使用的 $store.getters.某塊名.xxx,原因為這里是
this.$store.getters的值為
{主模塊名:屬性字模塊名/xxx: 屬性
}
// 我們$store.getters.字模塊名/xxx會報錯因為有 / 這個圖書字符。
// 因此使用$store.getters['模塊名/xxx']
② 通過 mapGetters 映射
默認根級別的映射 mapGetters([ 'xxx' ])
子模塊的映射 mapGetters('模塊名', ['xxx','xxx'...])
- 需要開啟命名空間
10.8 代碼演示
修改src/components/Son1.vue
<template><div class="box"><h2>Son1 子組件 使用原始方式獲取</h2><h4>$store.state.user: {{$store.state.user}}</h4><button @click="$store.commit('user/updateName', '李四')">修改名字</button><button @click="$store.dispatch('user/updateNameAsync', '王五')">1秒后修改</button><button @click="$store.commit('user/updateAge', 10)">年齡加10</button><h4>$store.getters["user/ageAddTen"]: {{$store.getters["user/ageAddTen"]}}</h4></div>
</template><script>
export default {computed: {},methods: {},mounted() {}}
</script><style lang="css" scoped>
.box{border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
修改src/components/Son2.vue
<template><div class="box"><h2>Son2 子組件 使用輔助函數方式獲取</h2><h4>theme的值為: {{theme}}</h4><h4>desc的值為: {{desc }}</h4><button @click="updateTheme('light')">修改背景色</button><button @click="updateThemeAsync('blue')">一秒后修改背景色</button><h4>desc+theme的值為:{{ themeDesc }}</h4></div>
</template><script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {computed: {...mapState('setting',["desc", "theme"]),...mapGetters('setting',["themeDesc"]),},methods: {...mapMutations('setting',["updateTheme"]),...mapActions('setting',["updateThemeAsync"]),}
};
</script><style lang="css" scoped>
.box {border: 3px solid #ccc;width: 400px;padding: 10px;margin: 20px;
}
h2 {margin-top: 10px;
}
</style>
運行結果
總結
流程圖
不使用分模塊的寫法
使用分模塊寫法
開啟嚴格模式
在Vuex的根節點文件src/store/index.js
中開啟。
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import setting from './modules/setting'
Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: { },mutations: {},actions: {},getters: {},modules: {user,setting}})
export default store
開啟命名空間
在子模塊src/store/modules/模塊.js
中開啟。
const state = {}const mutations = {}const actions = {}const getters = {}export default {// 開啟命名空間namespaced: true,state,mutations,actions,getters
}