Vuex(一) —— 集中式的狀態管理倉庫

目錄

  • Vue組件間通信方式回顧
    • 組件內的狀態管理流程
    • 組件間通信方式
      • 父組件給子組件傳值 (最簡單的一種方式)
      • 子組件給父組件傳值
      • 不相關組件之間傳值
      • 其他常見方式($ref)
  • 簡易的狀態管理方案
    • 上面組件間通信方式的問題
    • 集中式的狀態管理方案
  • Vuex
    • 什么是Vuex?
    • 什么情況下使用Vuex?
      • 非必要的情況不要使用Vuex
      • 中大型單頁應用程序使用更好
    • Vuex核心概念回顧
    • Vuex基本結構
    • State的使用
    • Getter的使用
    • Mutation的使用
      • Mutation的調試
        • 時光旅行
        • 狀態回滾
        • 提交改變
    • Actions的使用
    • Modules的使用
      • 模塊定義
      • 模塊注冊
      • 模塊使用
      • 添加命名空間
    • Vuex嚴格模式
    • Vuex插件
      • 插件的使用

年底最后一天來本來沒有想更博客,但是留下我今年還在努力學習的印記哈哈。看了半天這居然是我第一個關于vue的博客,努力不算晚。先來對這個博客的內容進行一下梳理,有興趣的小伙伴可以跳到自己感興趣的地方,這個博客比較長,長文警告。

image

Vue組件間通信方式回顧

組件內的狀態管理流程

Vue最核心的兩個功能:數據驅動組件化

使用基于組件化的開發,可以提高開發效率,帶來更好的可維護性。

new Vue({// state 組件內部都可以管理自己的內部狀態data () {return {count: 0}},// view 視圖,每個組件都有自己的視圖,把狀態綁定到視圖上,當用戶和視圖交互的時候,可能會更改狀態template: `<div>{{ count }}</div>`,// actions 行為,這里描述的是單個組件內部的狀態管理,實際開發中可能多個組件可以共享狀態methods: {increment () {this.count++}}
})

這里說的狀態管理 —— 是通過狀態,集中管理和分發,解決多個組件共享狀態的問題。

  • state:狀態,數據源。
  • view:視圖。通過把狀態綁定到視圖呈現給用戶
  • actions:用戶和視圖交互改變狀態的方式
image

圖中表明,狀態綁定到視圖上呈現給用戶,用戶通過與視圖交互改變狀態,之后改變了的狀態再綁定到視圖會后呈現給用戶。
單向的數據流程很簡單清晰,但是多個組件共享數據會破壞這種簡單的結構。

組件間通信方式的回顧

大多數情況下,組件都不是孤立存在的,他們需要共同協作構成一個復雜的業務功能,在Vue中,為不同的組件關系提供了不同的通信規則。

常見的組件間通信的方式有:

父組件給子組件傳值 (最簡單的一種方式)
  • 父組件中給子組件通過相應屬性傳值
  • 子組件通過props接受數據
<!--子組件-->
<template><div><h1>Props Down Child</h1><h2>{{ title }}</h2></div>
</template><script>
export default {// 子組件中通過props來接收父組件傳的值// props可以是數組也可以是對象// 如果想約定傳值的類型用對象,這里title定了是string類型,如果傳number類型會報錯// props: ['title'],props: {title: String}
}
</script><style></style>
<!--父組件-->
<template><div><h1>Props Down Parent</h1><!--2. 使用子組件的使用通過屬性給子組件傳值,這里也可以是表達式,綁定data中的成員--><child title="My journey with Vue"></child></div>
</template><script>
import child from './01-Child'
export default {// 1. 注冊子組件components: {child}
}
</script><style></style>
子組件給父組件傳值
  • 子組件通過自定義事件,用$emit觸發的時候攜帶參數給父組件傳值
  • 父組件通過$on注冊子組件內部觸發的事件,并接收傳遞的數據,行內可以通過$event獲取事件傳遞的參數 (事件處理函數中是不這么使用的)
<!--子組件-->
<template><div><h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1><button @click="handler">文字增大</button></div>
</template><script>
export default {// 通過props接收父組件傳的默認字體大小props: {fontSize: Number},methods: {handler () {// 當點擊按鈕的時候,觸發自定義事件enlargeText放大字體,讓字體放大0.1// this是當前子組件對象,this.$emit這個是由子組件觸發的自定義事件,當注冊事件的時候要給子組件注冊該事件this.$emit('enlargeText', 0.1)}}
}
</script><style></style>
<!--父組件-->
<template><div><!--父組件將fontSize進行綁定--><h1 :style="{ fontSize: hFontSize + 'em'}">Event Up Parent</h1>這里的文字不需要變化<!--使用子組件,通過v-on給子組件設置了自定義方法enlargeText--><child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child><child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child><!--還有一種在行內獲取值的方式,獲取自定義事件傳遞數據的時候,直接通過$event獲取,這個值是觸發事件傳遞的0.1--><child :fontSize="hFontSize" v-on:enlargeText="hFontSize += $event"></child></div>
</template><script>
import child from './02-Child'
export default {components: {child},data () {return {hFontSize: 1}},methods: {// 子組件把值傳遞給了父組件,父組件通過參數接收到了值,進行運算enlargeText (size) {this.hFontSize += size}}
}
</script><style></style>
不相關組件之間傳值
  • 也是使用自定義事件的方式,但是因為沒有父子關系,所以不能通過子組件觸發傳值,所以這里需要使用eventBus,即創建一個公共的Vue實例,這個實例的作用是作為事件總線,或者事件中心。
  • eventBus:創建一個Vue實例,這個實例并不是用來展示內容,所以沒有傳遞任何的選項,我們使用他的目的是使用$emit$on,用來觸發和注冊事件。
  • 觸發事件組件:通過$emit觸發事件,并傳遞參數
  • 注冊事件組件:通過$on注冊事件,接收參數進行處理
// eventbus.js
import Vue from 'vue'
export default new Vue()
<!--組件一:觸發事件-->
<template><div><h1>Event Bus Sibling01</h1><div class="number" @click="sub">-</div><input type="text" style="width: 30px; text-align: center" :value="value"><div class="number" @click="add">+</div></div>
</template><script>
// 這個組件要觸發事件,將事件中心導入
import bus from './eventbus'export default {
// props參數numprops: {num: Number},// 因為props的值不能隨便改動,所以傳遞給valuecreated () {this.value = this.num},data () {return {value: -1}},methods: {// 減值操作,判斷不能為0sub () {if (this.value > 1) {this.value--// 觸發bus的自定義事件numchange,并把value當參數傳遞出去bus.$emit('numchange', this.value)}},// 加值操作,和減值類似add () {this.value++bus.$emit('numchange', this.value)}}
}
</script><style>
.number {display: inline-block;cursor: pointer;width: 20px;text-align: center;
}
</style>
<!--組件二:定義-->
<template><div><h1>Event Bus Sibling02</h1><div>{{ msg }}</div></div>
</template><script>
// 因為要注冊事件,所以將事件中心導入
import bus from './eventbus'
export default {data () {return {msg: ''}},created () {// 通過bus注冊了numchange事件,事件處理函數中接收事件觸發時候傳遞的參數,進行展示bus.$on('numchange', (value) => {this.msg = `您選擇了${value}件商品`})}
}
</script><style></style>
<!--App.vue-->
<template><div id="app"><h1>不相關組件傳值</h1><sibling0301 :num="num"></sibling0301><sibling0302></sibling0302></div>
</template><script>
import sibling0301 from './components/03-event-bus/03-Sibling-01'
import sibling0302 from './components/03-event-bus/03-Sibling-02'export default {name: 'App',components: {sibling0301,sibling0302,},data () {return {num: 1}}
}
</script><style></style>
其他常見方式
  • $root$parent$children$ref,通過這幾種屬性獲取根組件成員,實現組件之間的通信。但這些都是不被推薦的方式。只有當項目很小,或者在開發自定義組件的時候,才會使用到。如果是大型項目的話,還是推薦使用Vuex管理狀態。

下面舉例通過$refs獲取子組件的狀態,其他屬性可以自己查看文檔。

ref的兩個作用

  1. 在普通HTML標簽上使用ref,用$refs獲取到的是DOM對象
  2. 在組件標簽上使用ref,用$refs獲取到的是組件實例
<!--子組件,一個簡單的自定義組件,功能是能夠獲取焦點的自定義文本框。-->
<template><div><h1>ref Child</h1><!--這個input標簽上設置了ref屬性--><input ref="input" type="text" v-model="value"></div>
</template><script>
export default {data () {return {value: ''}},methods: {focus () {// 通過this.$refs.input獲取input的DOM對象,并調用其focus方法讓文本框獲取焦點this.$refs.input.focus()}}
}
</script><style></style>
<!--父組件,-->
<template><div><h1>ref Parent</h1><!--在子組件的標簽上設置了ref--><child ref="c"></child></div>
</template><script>
import child from './04-Child'
export default {components: {child},mounted () {// 這里想要拿到子組件的話,必須要等組件渲染完畢,所以這里在mounted函數下// 這里通過this.$refs.c就是子組件對象,拿到這個對象就可以訪問其屬性和方法// 這里調用子組件方法讓其內部獲取焦點this.$refs.c.focus()// 通過value屬性給文本框賦值this.$refs.c.value = 'hello input'}
}
</script><style></style>

還是一句話不建議使用,如果濫用這種方式的話可以造成狀態管理的混亂。

簡易的狀態管理方案

上面組件間通信方式的問題

如果多個組件之間需要共享狀態,使用之前演示的方式雖然都可以實現,但是比較麻煩,而且多個組件之間進行傳值,很難跟蹤到數據的變化。如果出現問題的話,很難定位問題。當遇到多個組件需要共享狀態的時候,典型的場景如購物車,我們使用之前介紹的方案都不合適,可能會遇到以下的問題:

  • 多個視圖依賴同一狀態,如果多層嵌套的組件依賴同一狀態,使用父子組件傳值可以實現,但是非常麻煩而且不易管理。
  • 來自不同視圖的行為需要變更同一狀態,我們可以通過父子組件的方式對狀態進行修改,或者通過事件機制來改變,或者同步狀態的變化,以上這些方式非常的脆弱,通常會導致產生無法維護的代碼。

集中式的狀態管理方案

為了解決這些問題,我們把不同組件的共享狀態抽取出來,存儲到一個全局對象中并且將來使用的時候保證其實響應式的。這個對象創建好之后里面有全局的狀態和修改狀態的方法,我們的任何組件都可以獲取和通過調用對象中的方法修改全局對象中的狀態 (組件中不允許直接修改對象的state狀態屬性)。

把多個組件的狀態放到一個集中的地方存儲,并且可以檢測到數據的更改,這里先不使用Vuex,我們自己先進行一個簡單的實現。

  1. 創建一個全局的store.js

集中式的狀態管理,所有的狀態都在這里。這個模塊中導出了一個對象,這對象就是狀態倉庫且全局唯一的對象,任何組件都可以導入這個模塊使用

這里面有state,還有actionsstate是用來存儲狀態,actions是用戶交互更改視圖用的。還有一個debug的屬性,方便開發調試。

// store.js
export default {debug: true,state: {user: {name: 'xiaomao',age: 18,sex: '男'}},setUserNameAction (name) {if (this.debug) {console.log('setUserNameAction triggered:', name)}this.state.user.name = name}
}
  1. 在組件中導入
<!--組件A-->
<template><div><h1>componentA</h1><!--3. 可以在視圖中直接用點的方式顯示數據-->user name: {{ sharedState.user.name }}<button @click="change">Change Info</button></div>
</template><script>
// 1. 在組件中導入store
import store from './store'
export default {methods: {// 4. 當點擊按鈕的時候,調用store的方法,將值改為componentAchange () {store.setUserNameAction('componentA')}},data () {return {// 當前組件還可以有自己的私有狀態,存在privateState中privateState: {},// 2. 將store的state屬性賦值給shareStatesharedState: store.state}}
}
</script><style></style>
<!--組件B,用法與上面一樣,就是修改名字的時候值為componentB-->
<template><div><h1>componentB</h1>user name: {{ sharedState.user.name }}<button @click="change">Change Info</button></div>
</template><script>
import store from './store'
export default {methods: {change () {// 修改名字的時候改成了componentBstore.setUserNameAction('componentB')}},data () {return {privateState: {},sharedState: store.state}}
}
</script><style></style>

上面組件A和組件B都共享了全局的狀態,并且用戶都可以更改狀態。調試的時候,按A組件的按鈕兩者都變成了componentA,點B組件的按鈕兩者都變成了componentB

我們不在組件中直接修改狀態的值而是通過調用storeactions來修改值,這樣記錄的好處是: 能夠記錄store中所以state的變更,當可以實現記錄storestate的變更時候,就可以實現高級的調試功能。例如:timeTravel(時光旅行)和歷史回滾功能。

剛才使用的store,其實就類似于Vuex的倉庫。

當項目比較復雜,多個組件共享狀態的時候,使用組件間通信的方式比較麻煩,而且需要維護。這個時候我們可以使用集中式的狀態解決方案 —— Vuex

Vuex

好的終于進入了主題~~

什么是Vuex?

  • Vuex官網
  • Vuex 是專門為 Vue.js 設計的狀態管理庫,從使用的角度其實就是一個JavaScript
  • 它采用集中式的方式存儲需要共享的數據,如果狀態特別多的話不易管理,所以Vuex還提供了一種模塊的機制,按照模塊管理不同的狀態
  • 它的作用是進行狀態管理,解決復雜組件通信,數據共享
  • Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了time-travel時光旅行、歷史回滾、狀態快照、導入導出等高級調試功能

什么情況下使用Vuex?

非必要的情況不要使用Vuex

Vuex 可以幫助我們管理組件間共享的狀態,但是在項目中使用Vuex的話,我們需要了解Vuex中帶來的新的概念和一些API,如果項目不大,并且組件間共享狀態不多的情況下,這個時候使用Vuex給我們帶來的益處并沒有付出的時間多。此時使用簡單的 store 模式 或者其他方式就能滿足我們的需求。

中大型單頁應用程序使用更好

中大型單頁應用程序中,使用Vuex可以幫我們解決多個視圖依賴同一狀態來自不同視圖的行為需要變更同一狀態的問題。建議符合這些情況的業務,使用Vuex進行狀態管理,會給我們提供更好的處理組件的狀態,帶來的收益會更好些。例如典型案例:購物車。

注意:不要濫用Vuex,否則會讓業務變得更復雜。

Vuex核心概念回顧

下面這張圖展示了Vuex的核心概念并且展示了Vuex的整個工作流程

image
  • Store:倉庫,Store是使用Vuex應用程序的核心,每個應用僅有一個Store,它是一個容器,包含著應用中的大部分狀態,當然我們不能直接改變Store中的狀態,我們要通過提交Mutations的方式改變狀態。
  • State:狀態,保存在Store中,因為Store是唯一的,所以State也是唯一的,稱為單一狀態樹,這里的狀態是響應式的。
  • Getter:相當于Vuex中的計算屬性,方便從一個屬性派生出其他的值,它內部可以對計算的結果進行緩存,只有當依賴的狀態發生改變的時候,才會重新計算。
  • Mutation:狀態的變化必須要通過提交Mutation來完成。
  • Actions:與Mutation類似,不同的是可以進行異步的操作,內部改變狀態的時候都需要改變Mutation
  • Module:模塊,由于使用的單一狀態樹讓所有的狀態都會集中到一個比較大的對象中,應用變得很復雜的時候,Store對象就會變得相當臃腫,為了解決這些問題Vuex允許我們將Store分割成模塊,每個模塊擁有自己的StateMutationActionsGetter,甚至是嵌套的子模塊。

Vuex基本結構

使用vue-cli創建項目的時候,如果選擇了Vuex,會自動生成Vuex的基本結構。

// store.js
import Vue from 'vue'
// 導入Vuex插件
import Vuex from 'vuex'
// 通過use方法注冊插件
// 插件內部把Vuex的Store注入到了Vue的實例上
Vue.use(Vuex)
// 創建了Vuex的Store對象并且導出
export default new Vuex.Store({state: {...},mutations: {...},actions: {...},modules: {...}// 如果有需要還可以有getters
})
//App.js
// 導入store對象
import store from './store'
new Vue({router,// 在初始化Vue的時候傳入store選項,這個選項會被注入到Vue實例中// 我們在組件中使用的this.$store就是在這個地方注入的store,render: h => h(App)
}).$mount('#app')

State的使用

  1. 下載項目模板vuex-sample-temp ,npm install下載依賴,在store/index.js中定義兩個state
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {},actions: {},modules: {}
})
  1. App.vue中使用state,然后使用npm run serve查看結果
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count}} <br/>msg: {{ $store.state.ms }}</div>
</template>
<script>
  1. 每次使用變量都要前面寫 $store.state 很是麻煩,所以這里使用``Vuex內部提供的myState的函數,會幫我們生成狀態對應的計算屬性
<template><div id="app"><h1>Vuex - Demo</h1><!--4. 在使用的時候直接用計算屬性count和msg即可-->count: {{ count }} <br/>msg: {{ msg }}</div>
</template>
<script>// 1. 引入vuex的mapState模塊import { mapState } from 'vuex'export default {// 2. 在計算屬性中調用mapState函數computed: {// 3. mapState需要接收數組作為參數,數組的元素是需要映射的狀態屬性// 會返回一個對象,包含兩個對應屬性計算的方法// { count: state => state.count, msg: state => state.msg }// 然后這里使用擴展運算符展開對象,完成之后我們就有了count和msg兩個計算屬性...mapState(['count', 'msg'])}}
</script>
  1. 上面的方法比較簡潔但是如果這個組件中本身就有count或者msg屬性,就會造成名稱沖突,這個時候需要設置別名。
<template><div id="app"><h1>Vuex - Demo</h1><!-- 使用的時候直接使用別名即可 -->count: {{ num }} <br/>msg: {{ message }}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {computed: {// mapState可以傳對象,鍵是別名,值是映射的狀態屬性...mapState({ num: 'count', message: 'msg' })}
}
</script>

Getter的使用

Vuex中的getter就相當于組件中的計算屬性,如果想要對state的數據進行簡單的處理在展示,可以使用getter

這里用Vuexgetter處理而不是用組件中的計算屬性是因為狀態本身屬于Vuex,應該在其內部處理

  1. store.js中設置getters
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},// 與計算屬性的寫法一致getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {},actions: {},modules: {}
})
  1. App.vue中使用
<template><div id="app"><h1>Vuex - Demo</h1><h2>reverseMsg: {{ $store.getters.reverseMsg }}</h2><br/></div>
</template>
  1. 同樣那樣引用過于麻煩,那么和mapState一樣,使用內部的mapGetters,也是將其映射到組件的計算屬性,其用法和mapState一樣,也可以為了避免沖突使用對象設置別名。
<template><div id="app"><h1>Vuex - Demo</h1><h2>reverseMsg: {{ reverseMsg }}</h2><br/></div>
</template>
<script>
// 1. 引入vuex的mapGetters模塊
import { mapGetters } from 'vuex'
export default {// 2. 在計算屬性中調用mapGetters函數computed: {// 3. 用法與mapState一致,這里也可以使用對象設置別名...mapGetters(['reverseMsg'])}
}
</script>

Mutation的使用

狀態的修改必須提交MutationMutation必須是同步執行的。

  1. 當用戶點擊按鈕的時候,count值進行增加,先在store.js中寫Mutation函數
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {// 增加函數,接收兩個參數// 第一個state狀態// 第二個是payload載荷,payload是mutations的時候提交的額外參數,可以是對象,這里傳遞的是數字increate (state, payload) {state.count += payload}},actions: {},modules: {}
})
  1. App.vue中設置按鈕,并注冊事件
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Mutation</h2><!-- 給按鈕注冊點擊事件,點擊的時候調用commit提交Mutation,第一個參數是調用的方法名,第二個參數是payload,傳遞的數據 --><button @click="$store.commit('increate', 2)">Mutation</button></div>
</template>
  1. 點擊按鈕的時候,count的值每次+2
  2. 下面進行寫法優化,使用map方法將當前的mutation映射到methods中,其依舊會返回一個對象,這個對象中存儲的是mutation中映射的方法
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Mutation</h2><!-- 這里直接寫方法,,第一個參數state不需要傳遞,后面傳payload參數為3 --><button @click="increate(3)">Mutation</button></div>
</template>
<script>
// 1. 引入vuex的mapMutations模塊
import { mapMutations } from 'vuex'
export default {// 2. methods中調用mapMutations方法methods: {...mapMutations(['increate'])}
}
</script>
Mutation的調試

運行到4之后,這時看一下devtools看一下時光旅行和歷史回滾,下面是初始狀態

image

點一下按鈕之后就增加了一個記錄,還顯示了改變之后的數據

image

如果數據不對,可以進行調試。

時光旅行

然后多點幾下,進行時光旅行。

image

點擊按鈕之后,狀態就變成了之前那個狀態,這個功能也是為了方便調試

image
狀態回滾

這個圖標就是狀態回滾

image

點擊之后,代碼就回到了沒有執行這一步的狀態

image
提交改變

下面那個按鈕的意思是將這次提交作為最后一次提交

image

點擊之后,base State變成了那次的狀態,其他的狀態以這個作為起始點

image

Actions的使用

如果有異步的修改,需要使用actions,在actions中可以執行異步操作,當異步操作結束后,如果需要更改狀態,還需要提交Mutation

  1. actions中添加方法increateAsync
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {increate (state, payload) {state.count += payload}},actions: {// actions中的方法有兩個參數:第一個參數是context上下文,這個對象中有state,commit,getters等成員,第二個參數是payLoadincreateAsync (context, payLoad) {setTimeout(() => {context.commit('increate', payLoad)}, 2000)}},modules: {}
})
  1. App.vue中使用dispatchactions的方法都要用這個
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Actions</h2><!--這里使用了dispatch--><button @click="$store.dispatch('increateAsync',5)">Action</button></div>
</template>
  1. 進行優化,這個時候引入mapActions
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Actions</h2><button @click="increateAsync(5)">Action</button></div>
</template>
<script>
// 1. 引入vuex的mapActions模塊
import { mapActions } from 'vuex'
export default {methods: {// 這個是對Actions的方法的映射,把this.increateAsync映射到this.$store.dispatch...mapActions(['increateAsync'])}
}
</script>

Modules的使用

模塊可以讓我們把單一狀態樹拆分成多個模塊,每個模塊都可以擁有自己的statemutationactiongetter甚至嵌套子模塊。

模塊定義

store文件夾中,創建一個modules文件夾,里面每一個js文件就是一個模塊,下面是每一個模塊的定義格式

const state = {products: [{ id: 1, title: 'iPhone 11', price: 8000 },{ id: 2, title: 'iPhone 12', price: 10000 }]
}
const getters = {}
const mutations = {setProducts (state, payload) {state.products = payload}
}
const actions = {}export default {namespaced: false,state,getters,mutations,actions
}
模塊注冊
  1. 先導入這個模塊
import products from './modules/products'
import cart from './modules/cart'
  1. 后來在modules選項中注冊,注冊之后這里會把模塊掛載到storestate中,這里可以通過store.state.products訪問到products模塊中的成員,還把的模塊中的mutation成員記錄到了store的內部屬性_mutation中,可以通過commit直接提交mutation
export default new Vuex.Store({state: {},getters: {},mutations: {},actions: {},modules: {products,cart}
})
模塊使用
  1. App.vue中使用,state就點出來,mutation還是用commit方法
<template><div id="app"><h1>Vuex - Demo</h1><h2>Modules</h2><!--第一個products是products模塊,第二個products是模塊的state的products屬性-->products: {{ $store.state.products.products }} <br/><button @click="store.commit('setProducts',[])">Mutation</button></div>
</template>
添加命名空間

因為每個模塊中的mutation是可以重名的,所以推薦使用命名空間的用法,方便管理。

  1. 在開啟命名空間的時候,在模塊的導出部分添加namespaced
const state = {}
const getters = {}
const mutations = {}
const actions = {}export default {// true就是開啟,false或者不寫就是關閉namespaced: false,state,getters,mutations,actions
}
  1. 使用的時候在App.vue中要設置state是模塊中出來的,如果沒有命名空間,就是全局的state的。
<template><div id="app"><h1>Vuex - Demo</h1>products: {{ products }} <br/><button @click="setProducts([])">Mutation</button></div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {computed: {// 模塊中的state,第一個參數寫模塊名稱,第二個參數寫數組或者對象...mapState('products', ['products'])},methods: {// 模塊中的mutations,第一個寫模塊名稱,第二個寫數組或者對象...mapMutations('products', ['setProducts'])}
}
</script>

Vuex嚴格模式

所有的狀態變更必須提交mutation,但是如果在組件中獲取到$store.state.msg進行修改,語法層面沒有問題,卻破壞了Vuex的約定,且devTools也無法跟蹤到狀態的修改,開啟嚴格模式之后,如果在組件中直接修改state,會報錯。

  1. index.js,初始化Store的時候開啟嚴格模式
export default new Vuex.Store({strict: true,state: {...},...
}
  1. App.vue中使用直接賦值的語句
<template><div id="app"><h1>Vuex - Demo</h1><h2>strict</h2><button @click="$store.state.msg = 'hello world~'">strict</button></div>
</template>
  1. 點擊按鈕內容改變,但是控制臺會拋出錯誤
image

注意:不要在生產環境開啟嚴格模式,因為嚴格模式會深度檢測狀態樹,會影響性能。在開發模式中開啟嚴格模式,在生產環境中關閉嚴格模式

export default new Vuex.Store({strict: process.env.NODE_ENV !== 'production',state: {...
}

Vuex插件

  • Vuex插件就是一個函數,接收一個store的參數
  • 在這個函數中可以注冊函數讓其在所有的mutations結束之后再執行
插件的使用
  • 插件應該在創建Store之前去創建
  • subscribe函數
    • 作用是去訂閱store中的mutation
    • 他的回調函數會在每個mutation之后調用
    • subscribe會接收兩個參數,第一個是mutation,還可以區分模塊的命名空間,第二個參數是state,里面是存儲的狀態
  1. 定義插件
// 這個函數接收store參數
const myPlugin = store => {// 當store初始化后調用store.subscribe((mutation, state) => {// 每次 mutation 之后調用// mutation 的格式為 { type, payload }// type里面的格式是 "模塊名/state屬性"// state 的格式為 { 模塊一, 模塊二 }})
}
  1. Store中注冊插件
const store = new Vuex.Store({//...plugins: [myPlugin]
})


喜歡的朋友記得點贊、收藏、關注哦!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/85602.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/85602.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/85602.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

操作系統---內存管理上

文章目錄 1. 內存的基礎知識1.1 什么是內存&#xff0c;有何作用1.2 進程運行的基本原理1.2.1 指令的工作原理1.2.2 邏輯地址 VS 物理地址 1.3 如何實現地址轉換&#xff08;邏輯 -> 物理&#xff09;1.3.1 絕對裝入1.3.2 可重定位裝入&#xff08;靜態重定位&#xff09;1.…

醫學圖像處理期末復習

目錄 考試范圍第1章 緒論1.1 數字圖像處理的概念1.2 數字圖像處理的應用領域1、醫學領域2、其他領域 1.3 數字圖像處理基礎1.4 數字圖像基礎運算 第2章 醫學圖像灰度變換與空間濾波2.1 醫學圖像灰度變換線性灰度變換非線性灰度變換 2.2 直方圖均衡化√2.3 空間平滑濾波線性空間…

類圖:軟件世界的“建筑藍圖”

本文來自「大千AI助手」技術實戰系列&#xff0c;專注用真話講技術&#xff0c;拒絕過度包裝。 類圖&#xff08;Class Diagram&#xff09;&#xff1a;軟件世界的“建筑藍圖” 類圖&#xff08;Class Diagram&#xff09;是統一建模語言&#xff08;UML&#xff09; 中最重要…

利用DevEco Studio對RK3588的HiHopesOS-4.1.110(OpenHarmony)進行Qt程序編寫

文章目錄 熱身準備添加Qt庫運行qml程序 熱身 可以先看我這篇文章【DevEco Studio中使用Qt&#xff0c;編寫HarmonyOS程序】 準備 板子的主要信息 目前由于系統版本&#xff08;API 11&#xff09;及其他原因&#xff0c;只能用4.1版本的DevEcoStudio來編寫&#xff0c;更高…

設計模式精講 Day 5:原型模式(Prototype Pattern)

【設計模式精講 Day 5】原型模式&#xff08;Prototype Pattern&#xff09; 文章內容 在“設計模式精講”系列的第5天&#xff0c;我們將深入講解原型模式&#xff08;Prototype Pattern&#xff09;。作為創建型設計模式之一&#xff0c;原型模式通過復制已有對象來創建新對…

深度學習——第2章習題2-1分析為什么平方損失函數不適用于分類問題

深度學習——第2章習題2-1 《神經網絡與深度學習》——邱錫鵬 2-1 分析為什么平方損失函數不適用于分類問題。 平方損失函數&#xff08;Quadratic Loss Function&#xff09;經常用在預測標簽y為實數值的任務中&#xff0c;定義為 L ( y , f ( x ; θ ) ) 1 2 ( y ? f (…

【Linux】運行腳本后打屏同時保存到本地

命令&#xff1a; sh run.sh 2>&1 | tee output.log sh run.sh 2>&1 | tee output_$(date "%Y%m%d%H%M").log作用&#xff1a;運行腳本&#xff0c;并同時將輸出&#xff08;包括標準輸出和錯誤輸出&#xff09;顯示到終端&#xff0c;并保存到文件中…

Spark 在小眾日常場景中的實戰應用:從小店數據到社區活動

Spark 在小眾日常場景中的實戰應用&#xff1a;從小店數據到社區活動? 提起 Spark&#xff0c;大家往往會聯想到大型互聯網公司的數據處理、金融行業的復雜分析。但實際上&#xff0c;Spark 在許多小眾、貼近生活的場景中也能大顯身手。結合學習與實踐&#xff0c;我探索了 S…

mysql 執行計劃 explain命令 詳解

explain id &#xff1a;select查詢的序列號&#xff0c;包含一組數字&#xff0c;表示查詢中執行select子句或操作表的順序select_type&#xff1a;查詢類型 或者是 其他操作類型table &#xff1a;正在訪問哪個表partitions &#xff1a;匹配的分區type &#xff1a;訪問的類…

讓大模型“更懂人話”:對齊訓練(RLHF DPO)全流程實戰解析

網羅開發 &#xff08;小紅書、快手、視頻號同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企業從事人工智能項目研發管理工作&#xff0c;平時熱衷于分享各種編程領域的軟硬技能知識以及前沿技術&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

GO 原子操作面試題及參考答案

Go 的 sync/atomic 包和 sync.Mutex 的根本區別是什么&#xff1f; Go 語言中的 sync/atomic 包和 sync.Mutex 都用于處理并發編程中的同步問題&#xff0c;但它們的實現機制、應用場景和性能特性存在根本差異。理解這些差異對于編寫高效、安全的并發代碼至關重要。 sync/atomi…

MATLAB 山脊圖繪制全解析:從數據生成到可視化進階

一、引言&#xff1a;當數據分布擁有「層次感」—— 山脊圖的魅力? 在數據可視化的世界里&#xff0c;我們常常需要同時展示多個分布的形態差異。傳統的重疊密度圖雖然能呈現整體趨勢&#xff0c;但當分布數量較多時&#xff0c;曲線交疊會讓畫面變得雜亂。這時候&#xff0c…

跨境電商每周資訊—6.16-6.20

1. Instagram 在亞太地區逐漸超越 TikTok 在整個亞太地區&#xff0c;Instagram用戶數量正逐漸超過TikTok。預計2025年日本Instagram用戶數量將增至4440萬&#xff0c;印度今年用戶數量將增長10%&#xff0c;領跑亞太。與之形成對比的是&#xff0c;TikTok在一些國家增長速度放…

計算機網絡 網絡層:數據平面(一)

前一節學習了運輸層依賴于網絡層的主機到主機的通信服務&#xff0c;提供各種形式的進程到進程的通信。了解這種主機到主機通信服務的真實情況&#xff0c;是什么使得它工作起來的。 在本章和下一章&#xff0c;將學習網絡層實際是怎樣實現主機到主機的通信服務。與運輸層和應用…

Suna本地部署詳細教程

一、安裝基礎環境 # 1、創建環境 conda create -n suna python3.11.7# 2、激活虛擬環境 conda activate suna# 3、安裝jupyter和ipykernel pip install jupyter ipykernel# 4、將虛擬環境添加到jupyter # python -m ipykernel install --user --namemyenv --display-name"…

LeetCode 每日一題打卡|若谷的刷題日記 3day--最長連續序列

1.最長連續序列 題目&#xff1a; 給定一個未排序的整數數組 nums &#xff0c;找出數字連續的最長序列&#xff08;不要求序列元素在原數組中連續&#xff09;的長度。 請你設計并實現時間復雜度為 O(n) 的算法解決此問題。 示例 1&#xff1a; 輸入&#xff1a;nums [1…

EfficientVLA:面向視覺-語言-動作模型無訓練的加速與壓縮

25年6月來自上海交大、哈工大、西安交大和電子科大&#xff08;成都&#xff09;的論文“EfficientVLA: Training-Free Acceleration and Compression for Vision-Language-Action Models”。 視覺-語言-動作 (VLA) 模型&#xff0c;特別是基于擴散的架構&#xff0c;展現出具…

wireshark抓包分析TCP數據包

1、直接從TCP的三次握手開始說起 三次握手就是客戶與服務器建立連接的過程 客戶向服務器發送SYN(SEQ=x)報文,然后就會進入SYN_SEND狀態服務器收到SYN報文之后,回應一個SYN(SEQ=y)ACK(ACK=x+1)報文,然后就會進入SYN_RECV狀態客戶收到服務器的SYN報文,回應一個ACK(AC…

同等學力申碩-計算機統考-歷年真題和備考經驗

同等學力申請碩士學位考試是比較適合在職人員的提升學位方式&#xff0c;了解過的人應該都知道&#xff0c;現在社會的競爭壓力越來越大&#xff0c;為了提高職業生存能力&#xff0c;提升學位在所難免。 一、已有計算機統考歷年真題資料 報名過同等學力申碩計算機專業的朋友都…

OSI網絡通信模型詳解

OSI 模型就是把這整個過程拆解成了 7 個明確分工的步驟&#xff0c;每一層只負責自己那一攤事兒&#xff0c;這樣整個系統才能順暢運轉&#xff0c;出了問題也容易找到“鍋”在誰那。 核心比喻&#xff1a;寄快遞 &#x1f4e6; 想象你要把一份重要的禮物&#xff08;你的數據…