## 一、Vuex
Vuex 是一個專為 Vue.js 應用程序開發的**狀態管理模式**。
官網:[https://next.vuex.vuejs.org/](https://next.vuex.vuejs.org/)
##### **主要功能:**
1、vuex可以實現vue不同組件之間的狀態共享 (解決了不同組件之間的數據共享)
2、可以實現組件里面數據的持久化。
##### **Vuex的幾個核心概念:**
State
Getters
Mutations
Actions
Modules
### 二、Vuex的基本使用
#### 1、安裝依賴
##### NPM
~~~
npm install vuex@next --save
~~~
##### yarn
~~~
yarn add vuex@next --save
~~~
#### 2、src目錄下面新建一個vuex的文件夾,vuex 文件夾里面新建一個store.js
~~~
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
count: 1
}
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store;
~~~
#### 3、main.ts中掛載Vuex
~~~
import { createApp } from 'vue'
import App from './App.vue'
import route from './routes'
import store from './vuex/store'
let app=createApp(App);
//掛載路由
app.use(route)
//掛載vuex
app.use(store)
app.mount('#app')
~~~
#### 4、獲取 修改state里面的數據
~~~
<template>
<div>
增加新聞--{{count}}
<br>
<button @click="incCount">改變Vuex里面的count</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
data(){
return{}
},methods:{
incCount(){
this.$store.commit('increment')
}
},
computed:{
count():number{
return this.$store.state.count
}
}
})
</script>
~~~
### 三、Vuex中的State
State在Vuex中主要用于存儲數據,State是存儲在 Vuex 中的數據和 Vue 實例中的 `data` 遵循相同的規則。
~~~
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
count: 1,
list:['馬總','雷總','王總']
}
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store;
~~~
#### 3.1、第一種獲取State的方法(不推薦)
用到的組件里面引入store,然后計算屬性里面獲取
~~~
computed: {
count () {
return store.state.count
}
}
~~~
#### 3.2、第二種獲取State的方法
由于全局配置了Vuex `app.use(store)`,所以直接可以通過下面方法獲取store里面的值。
~~~
computed: {
count () {
return this.$store.state.count
}
}
~~~
#### 3.3、第三種獲取State的方法-通過`mapState`助手
**方法 1:**
~~~
<template>
<div>修改新聞--{{ count }}</div>
<br />
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
<br />
</template>
<script>
import { defineComponent } from "vue";
import { mapState } from "vuex";
export default defineComponent({
data() {
return {};
},
methods: {},
computed: {
...mapState({
count: (state) => state.count,
list: (state) => state.list,
}),
},
});
</script>
~~~
**方法 2:**
~~~
<template>
<div>修改新聞--{{ count }}</div>
<br>
<ul>
<li v-for="(item,index) in list" :key="index">{{item}}</li>
</ul>
<br>
</template>
<script>
import { defineComponent } from "vue";
import { mapState } from "vuex";
export default defineComponent({
data() {
return {};
},
methods: {},
computed: {
...mapState([
"count",
"list"
]),
},
});
</script>
~~~
### 四:Vuex中的Getters
Getter有點類似我們前面給大家講的計算屬性。
Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。
#### 4.1 、定義Getter
~~~
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
~~~
#### 4.2、訪問Getter的第一種方法
Getter 會暴露為 `store.getters` 對象,你可以以屬性的形式訪問這些值:
~~~
store.getters.doneTodos
~~~
#### **4.3、訪問Getter的第二種方法**
~~~
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
~~~
#### 4.4、訪問Getter的第四種方法 通過`mapGetters`輔助函數
~~~
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用對象展開運算符將 getter 混入 computed 對象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
~~~
如果你想將一個 getter 屬性另取一個名字,使用對象形式:
~~~
...mapGetters({
// 把 `this.doneCount` 映射為 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
~~~
### 五、Vuex中的Mutations
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 **事件類型 (type)** 和 一個 **回調函數 (handler)**。這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數。
#### 4.1、定義Mutations 觸發Mutations里面的方法
~~~
const store = createStore({
state: {
count: 1
},
mutations: {
increment (state) {
// mutate state
state.count++
}
}
})
~~~
觸發mutations里面的方法:
~~~
store.commit('increment')
~~~
#### 4.2、執行方法傳入參數:
~~~
mutations: {
increment (state, n) {
state.count += n
}
}
~~~
~~~
store.commit('increment', 10)
~~~
#### 4.3 對象方式提交數據
~~~
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
~~~
~~~
store.commit({
type: 'increment',
amount: 10
})
~~~
#### 4.4 在組件中提交 Mutation
~~~
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`
// `mapMutations` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`
})
}
}
~~~
### 六、Vuex中的Actions
Action 類似于 mutation,不同在于:
* Action 提交的是 mutation,而不是直接變更狀態。
* Action 可以包含任意異步操作。
#### 6.1、定義Action
~~~
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
~~~
另一種寫法
~~~
actions: {
increment ({ commit }) {
commit('increment')
}
}
~~~
#### 6.2、分發 Action(觸發Action中的方法)
~~~
store.dispatch('increment')
~~~
乍一眼看上去感覺多此一舉,我們直接分發 mutation 豈不更方便?實際上并非如此,還記得 **mutation 必須同步執行**這個限制么?Action 就不受約束!我們可以在 action 內部執行**異步**操作:
~~~
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
~~~
Actions 支持同樣的載荷方式和對象方式進行分發:
~~~
// 載荷方式
store.dispatch('incrementAsync', {
amount: 10
})
// 對象方式
store.dispatch({
type: 'incrementAsync',
amount: 10
})
~~~
來看一個更加實際的購物車示例,涉及到**調用異步 API** 和**分發多重 mutation**:
~~~
actions: {
checkout ({ commit, state }, products) {
// 把當前購物車的物品備份起來
const savedCartItems = [...state.cart.added]
// 發出結賬請求,然后樂觀地清空購物車
commit(types.CHECKOUT_REQUEST)
// 購物 API 接受一個成功回調和一個失敗回調
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失敗操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
~~~
注意我們正在進行一系列的異步操作,并且通過提交 mutation 來記錄 action 產生的副作用(即狀態變更)
#### 6.3 在組件中分發 Action
你在組件中使用 `this.$store.dispatch('xxx')` 分發 action,或者使用 `mapActions` 輔助函數將組件的 methods 映射為 `store.dispatch` 調用(需要先在根節點注入 `store`):
~~~
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // map `this.increment()` to `this.$store.dispatch('increment')`
// `mapActions` also supports payloads:
'incrementBy' // map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // map `this.add()` to `this.$store.dispatch('increment')`
})
}
}
~~~
#### 6.4 組合 Action
Action 通常是異步的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?
首先,你需要明白 `store.dispatch` 可以處理被觸發的 action 的處理函數返回的 Promise,并且 `store.dispatch` 仍舊返回 Promise:
~~~
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
~~~
現在你可以:
~~~
store.dispatch('actionA').then(() => {
// ...
})
~~~
在另外一個 action 中也可以:
~~~
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
~~~
最后,如果我們利用 [async / await](https://tc39.github.io/ecmascript-asyncawait/),我們可以如下組合 action:
~~~
// assuming `getData()` and `getOtherData()` return Promises
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // wait for `actionA` to finish
commit('gotOtherData', await getOtherData())
}
}
~~~
### 七、Modules
由于使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常復雜時,store 對象就有可能變得相當臃腫。
為了解決以上問題,Vuex 允許我們將 store 分割成**模塊(module)**。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割:
~~~
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> `moduleA`'s state
store.state.b // -> `moduleB`'s state
~~~
#### 模塊的局部狀態
對于模塊內部的 mutation 和 getter,接收的第一個參數是**模塊的局部狀態對象**。
~~~
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// `state` is the local module state
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
~~~
### 八 、Vuex項目結構
Vuex 并不限制你的代碼結構。但是,它規定了一些需要遵守的規則:
1. 應用層級的狀態應該集中到單個 store 對象中。
2. 提交**mutation**是更改狀態的唯一方法,并且這個過程是同步的。
3. 異步邏輯都應該封裝到**action**里面。
只要你遵守以上規則,如何組織代碼隨你便。如果你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。
對于大型應用,我們會希望把 Vuex 相關代碼分割到模塊中。下面是項目結構示例:
~~~
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我們組裝模塊并導出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
└── modules
├── cart.js # 購物車模塊
└── products.js # 產品模塊
~~~
### 九、Vuex結合組合式合成API
組合式api中沒有this.$store,可以使用useStore來替代
~~~
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
~~~
#### 9.1、組合式api中訪問state 和 getters
~~~
const store = new createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
~~~
~~~
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// access a state in computed function
count: computed(() => store.state.count),
// access a getter in computed function
double: computed(() => store.getters.double)
}
}
}
~~~
#### 9.2、組合式api中訪問 Mutations and Actions
~~~
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// access a mutation
increment: () => store.commit('increment'),
// access an action
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
~~~
### 十、Vue+Typescript的項目里面集成Vuex
首先需要在vue項目中集成typescript
~~~
vue add typescript
~~~
\*\*提示:\*\*如果配置完ts后調用this.$store有警告信息,請重啟vscode,或者安裝vue3的插件后重啟vscode充實
#### 一、修改store.js 為store.ts
#### 二、配置store.ts中的代碼
Vuex與TypeScript一起使用時,必須聲明自己的模塊擴充。
~~~
import { ComponentCustomProperties } from 'vue'
import { createStore,Store } from 'vuex'
//配置讓Vuex支持ts
declare module '@vue/runtime-core' {
//declare your own store states
interface State {
count: number,
list:string[]
}
// provide typings for `this.$store`
interface ComponentCustomProperties {
$store: Store<State>
}
}
const store = createStore({
state () {
return {
count: 1,
list:['馬總','雷總','王總']
}
},
mutations: {
increment (state:any):void {
state.count++
}
}
})
export default store;
~~~
#### 三、main.ts中掛載
~~~
import { createApp } from 'vue'
import App from './App.vue'
import route from './routes'
import store from './vuex/store'
let app=createApp(App);
//掛載路由
app.use(route)
//掛載vuex
app.use(store)
app.mount('#app')
~~~
#### 四、組件中使用掛載
~~~
<template>
<div>修改新聞--{{ count }}</div>
<br />
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
<br />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { mapState } from "vuex";
export default defineComponent({
data() {
return {};
},
methods: {},
computed: {
mylist():string[]{
return this.$store.state.list
},
...mapState({
count: (state:any) => state.count,
list: (state:any) => state.list,
})
},
});
</script>
~~~
- 空白目錄
- 第一節 Vue3.x教程、Vue3.x簡介、搭建Vue3.x環境、創建運行Vue3.x項目、分析Vue目錄結構
- 第二節 Vue3.x綁定數據、綁定html、綁定屬性、循環數據
- 第三節 Vue3.x中的事件方法入門、模板語法模板中類和樣式綁定
- 第四節 Vue3.x中的事件方法詳解、事件監聽、方法傳值、事件對象、多事件處理程序、事件修飾符、按鍵修飾符
- 第五節 Vue3.x中Dom操作$refs 以及表單( input、checkbox、radio、select、 textarea )結合雙休數據綁定實現在線預約功能
- 第六節 Vue3.x中使用JavaScript表達式 、條件判斷、 計算屬性和watch偵聽
- 第七節 Vue3.x 實現一個完整的toDoList(待辦事項) 以及類似京東App搜索緩存數據功能
- 第八節 Vue3.x中的模塊化以及封裝Storage實現todolist 待辦事項 已經完成的持久化
- 第九節 Vue3.x中的單文件組件 定義組件 注冊組件 以及組件的使用
- 第十節 Vue3.x父組件給子組件傳值、Props、Props驗證、單向數據流
- 第十一節 Vue3.x父組件主動獲取子組件的數據和執行子組件方法 、子組件主動獲取父組件的數據和執行父組件方法
- 第十二節 Vue3.x組件自定義事件 以及mitt 實現非父子組件傳值
- 第十三節 Vue3.x自定義組件上面使用v-mode雙休數據綁定 以及 slots以及 Prop 的Attribute 繼承 、禁用 Attribute 繼承
- 第十四節 Vue3.x中組件的生命周期函數(lifecycle)、 this.$nextTick、動態組件 keep-alive、Vue實現Tab切換
- 第十五節 Vue3.x中全局綁定屬性、使用Axios和fetchJsonp請求真實api接口數據、函數防抖實現百度搜索
- 第十六節 Vue3.x中的Mixin實現組件功能的復用 、全局配置Mixin
- 第十七節 Vue3.x Teleport、使用Teleport自定義一個模態對話框的組件
- 第十八節 Vue3.x Composition API 詳解
- 第十九節 Vue3.x中集成Typescript 使用Typescript
- 第二十節 Vue-Router 詳解
- 第二十節 Vuex教程-Vuex 中的 State Mutation Getters mapGetters Actions Modules