本文取之官網和其他文章結合自己的理解用簡單化的語言表達。用於自己的筆記記錄,也希望能幫到其他小伙伴理解,學習更多的前端知識。
-
Vuex 是什么?
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
官方文檔: https://vuex.vuejs.org/zh/
簡單的說,這個vuex可以用於存儲數據的狀態。例如:
我的個人信息是否完善的這個狀態,可以存儲在vuex中,在不同的頁面可以讀取、更改這個狀態。這樣就不用頁面之間傳參等方式。
當然也可以存儲一些信息,例如數組、對象、字符串都可以,類似前端的數據庫。如果只有這個特點的話,有的小伙伴會有疑問,那本地存儲也可以做到的,現在我們就看一下vuex與本地存儲的區別。
1.區別:vuex存儲在內存,localstorage(本地存儲)則以文件的方式存儲在本地,永久保存;sessionstorage( 會話存儲 ) ,臨時保存。localStorage和sessionStorage只能存儲字符串類型,對於復雜的對象可以使用ECMAScript提供的JSON對象的stringify和parse來處理
2.應用場景:vuex用於組件之間的傳值,localstorage,sessionstorage則主要用於不同頁面之間的傳值。
3.永久性:當刷新頁面(這里的刷新頁面指的是 --> F5刷新,屬於清除內存了)時vuex存儲的值會丟失,sessionstorage頁面關閉后就清除掉了,localstorage不會。
注:很多同學覺得用localstorage可以代替vuex, 對於不變的數據確實可以,但是當兩個組件共用一個數據源(對象或數組)時,如果其中一個組件改變了該數據源,希望另一個組件響應該變化時,localstorage,sessionstorage無法做到,原因就是區別1
原始網址:https://blog.csdn.net/sinat_36729274/article/details/87433615
- vuex安裝
安裝命令行:npm install vuex --save
安裝成功后,使用命令行:npm run dev 運行項目。接下來會在項目中使用vuex,首先,在src文件夾下新建一個名為store的文件夾,在store文件夾下新建index.js文件,index.js文件的內容分為:
import Vue from 'vue' // 引入vue import Vuex from 'vuex' // 引入vuex // 使用vuex Vue.use(Vuex) // 創建vuex實例 const store = new Vuex.Store({ }) export default store
然后在main.js文件中引入store文件夾下的index.js文件。main.js文件中內容如下:
import Vue from 'vue' import App from './App' import router from './router' import store from './store' // ------------ 引入store文件夾下面的index.js Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', store, // -------------- 將store 添加到vue的實例中 router, components: { App }, template: '<App/>' })
這樣我們就在項目中引入vuex了,那怎么使用vuex呢?
vuex的核心概念是以下幾個:
-
- State
- Getter
- Mutation
- Action
- Module
下面是具體來講一下vuex核心概念的使用方法吧。
- State
Vuex 使用單一狀態樹——是的,用一個對象就包含了全部的應用層級狀態。至此它便作為一個“唯一數據源 (SSOT)”而存在。這也意味着,每個應用將僅僅包含一個 store 實例。單一狀態樹讓我們能夠直接地定位任一特定的狀態片段,在調試的過程中也能輕易地取得整個當前應用狀態的快照。
以上是官網的說法,我的理解就是在state中聲明變量,可以在全部頁面都可以使用state中的變量。
現在,我們開始新建和使用state,在store文件夾下面新建state文件夾,state文件夾下面新建index.js文件,index.js文件內容如下:
const state = { name: 'weish', age: 22, todos: [ { id: 1, text: '1111', done: true }, { id: 2, text: '2222', done: false } ] }; export default state;
然后將state引入到store中,所以在store文件夾下的index.js內容如下:
import Vue from 'vue' // 引入vue import Vuex from 'vuex' // 引入vuex import state from './state' // ---------引入state // 使用vuex Vue.use(Vuex) // 創建vuex實例 const store = new Vuex.Store({ state // ---------將state添加到實例中 }) export default store
做完上面state中的聲明與store中的引入,我們就可以在頁面中使用state中的變量了。
HelloWorld.vue內容如下:
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>state:方法一:{{this.$store.state.age}}</h2> <h2>state:方法二:{{age2}}</h2> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ age2(){ return this.$store.state.age } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
使用輔助函數mapState時,HelloWorld.vue內容如下:
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>state:方法一:{{this.$store.state.age}}</h2> <h2>state:方法二:{{age2}}</h2> <h2>state:方法三:{{age3}}</h2> <h2>state:方法四:{{age4}}</h2> </div> </template> <script> // 輔助函數為 Vuex.mapState import { mapState } from 'vuex' export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed: mapState({ age2: 'age', //方法2 age3: (state) => state.age, // 方法3 age4:function () { return this.$store.state.age // 普通函數的使用store的方式,方法4 } }) //沒有使用mapState時,普通函數的寫法 // computed:{ // age5(){ // return this.$store.state.age //方法5 // }, // ...mapState(['name','age']) // 方法6 // } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
頁面效果如下:
- Getter
有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:
computed: { doneTodosCount () { return this.$store.state.todos.filter(todo => todo.done).length } }
如果有多個組件需要用到此屬性,我們要么復制這個函數,或者抽取到一個共享函數然后在多處導入它——無論哪種方式都不是很理想。
Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。
在store文件夾下新建getters文件夾,在getters文件夾下新建index.js,內容如下:
export const doneTodosCount = (state) => { return state.todos.filter(todo => todo.done).length; }
在store文件夾下index.js中引入getters
import Vue from 'vue' // 引入vue import Vuex from 'vuex' // 引入vuex import state from './state' import * as getters from './getters' // ---------引入 // 使用vuex Vue.use(Vuex) // 創建vuex實例 const store = new Vuex.Store({ state, getters // --------添加 }) export default store
可以通過屬性訪問:store.getters.doneTodosCount
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>getters: {{Count}}</h2> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ Count() { return this.$store.getters.doneTodosCount // -------1 } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
頁面效果如下:
使用輔助函數mapGetters,內容如下:
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>getters: {{Count}}</h2> <h2>getters:mapGetters {{doneTodosCount}}</h2> // -----------頁面中{{doneTodoCount}} </div> </template> <script> // 在單獨構建的版本中輔助函數為 Vuex.mapState 和mapGetter import { mapState,mapGetters } from 'vuex' // -----引入mapGetters export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ age(){ return this.$store.state.age }, ...mapState(['name','age','todos']), ...mapGetters(['doneTodosCount']), //-----------在computed使用mapGetter Count() { return this.$store.getters.doneTodosCount } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
- Mutation
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。mutations必須是同步函數。
在store文件夾下新增mutations文件夾,在mutations文件夾下新增index.js,內容如下:
export const CACHE_AGE = (state, param) => { // 當提交格式是簡單數據格式,例如:123,'123' state.age += param; } export const newName = (state,payload) => { // 當提交格式是對象格式 state.name = payload.newName }
在store文件夾下index.js中引入mutations
import Vue from 'vue' // 引入vue import Vuex from 'vuex' // 引入vuex import state from './state' import * as getters from './getters' import * as mutations from './mutations' // -----------引入mutations // 使用vuex Vue.use(Vuex) // 創建vuex實例 const store = new Vuex.Store({ state, getters, mutations // ---------添加到實例 }) export default store
在頁面中使用mutations修改state中age
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>state:方法一:{{this.$store.state.age}}</h2> <h2>getters: {{Count}}</h2> <h2>getters:mapGetters {{doneTodosCount}}</h2> <div><button @click="plus()">點我age加2</button></div> <h2>{{name}}</h2> <div><button @click="changeName()">點我name修改為Alise</button></div> </div> </template> <script> // 在單獨構建的版本中輔助函數為 Vuex.mapState import { mapState,mapGetters } from 'vuex' export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ age(){ return this.$store.state.age }, ...mapState(['name','age','todos']), ...mapGetters(['doneTodosCount']), Count() { return this.$store.getters.doneTodosCount } }, methods:{ plus(){ this.$store.commit('CACHE_AGE', 2) }, changeName(){ // this.$store.commit({type:'newName',newName:'Alise'}) //--------------對象風格提交格式 方法一 this.$store.commit('newName',{newName:'Alise'}) // -------------對象風格提交格式 方法二 } } } </script>
-
- 使用常量替代Mutation事件類型
把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目了然。用不用常量取決於你——在需要多人協作的大型項目中,這會很有幫助。但如果你不喜歡,你完全可以不這樣做。
在mutations文件夾下面新建mutation-types.js,內容如下:
export const CACHE_NAME_TYPE = 'CACHE_NAME_TYPE'
mutations文件夾下index.js,內容如下:
import { CACHE_NAME_TYPE } from './mutation-types' const mutations = { [CACHE_NAME_TYPE](state,payload){ state.name = payload.newName } } export default mutations // ps:如果mutation部分寫成這種格式,那么在store文件夾下的index.js中引入mutations的方式需要修改寫成 // import mutations from './mutations' // 而不是 import * as mutations from './mutations'
在頁面中使用mapMutations輔助函數:
<template> <div class="hello"> <h1>{{ msg }}</h1 <h2>state:name:{{name}}</h2> <div><button @click="changeName()">點我store的名字變成alise</button></div> <div><button @click="changeName1()">點我store的名字變成Mark</button></div> </div> </template> <script> // 在單獨構建的版本中輔助函數為 Vuex.mapState import { mapState,mapGetters,mapMutations } from 'vuex' // ------引入mapMutation export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ ...mapState(['name','age','todos']), ...mapGetters(['doneTodosCount']) }, methods:{ ...mapMutations(['CACHE_NAME']), // -------------在methods中映射,將this.CACHE_NAME()映射為this.$store.commit('CACHE_NAME') changeName(){ this.$store.commit('newName',{newName:'Alise'}) }, changeName1(){ this.CACHE_NAME({newName:'MARK'}) // -------使用 } } } </script>
- Action
action類似mutation,不同之處在於:
-
- action提交的mutation,而不是直接改變的狀態
- action可以包含任一異步操作
在store文件夾下新建actions文件夾,actions文件夾下新建index.js,內容如下:
export const setName = ({ commit }, param) => { commit('CACHE_NAME', param) }
在store文件夾下的index.js中引入actions
就可以在頁面中使用actions了,在頁面中調用actions的方法是:this.$store.dispatch('setName',{newName:'Tom'}),具體代碼如下:
<template> <div class="hello"> <h1>{{ msg }}</h1 <h2>state:name:{{name}}</h2> <div><button @click="changeName()">點我store的名字變成alise</button></div> <div><button @click="changeName1()">點我store的名字變成Mark</button></div> <div><button @click="changeName2()">點我store的名字通過actions方式變成Tom</button></div> </div> </template> <script> import { mapState,mapGetters,mapMutations } from 'vuex' export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ ...mapState(['name','age','todos']), ...mapGetters(['doneTodosCount']) }, methods:{ ...mapMutations(['CACHE_NAME']), changeName(){ this.$store.commit('newName',{newName:'Alise'}) }, changeName1(){ this.CACHE_NAME({newName:'MARK'}) }, changeName2(){ this.$store.dispatch('setName',{newName:'Tom'}) } } } </script>
使用mapActions輔助函數:
<template> <div class="hello"> <h1>{{ msg }}</h1 <h2>state:name:{{name}}</h2> <div><button @click="changeName()">點我store的名字變成alise</button></div> <div><button @click="changeName1()">點我store的名字變成Mark</button></div> <div><button @click="changeName2()">點我store的名字通過actions方式變成Tom</button></div> </div> </template> <script> import { mapState,mapGetters,mapMutations,mapActions } from 'vuex' // ------------添加mapActions export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ ...mapState(['name','age','todos']), ...mapGetters(['doneTodosCount']) }, methods:{ ...mapMutations(['CACHE_NAME']), ...mapActions(['setName']), // 修改二:在methods中添加...mapActions changeName(){ this.$store.commit('newName',{newName:'Alise'}) }, changeName1(){ this.CACHE_NAME({newName:'MARK'}) }, changeName2(){ // this.$store.dispatch('setName',{newName:'Tom'}) this.setName({newName:'Tom1111'}) // 修改三:使用this.setName提交修改mutation } } } </script>
actions中通常是異步操作,可以組合使用action,以處理更復雜的流程。可查看官網例子組合action,官網地址:https://vuex.vuejs.org/zh/guide/actions.html
- Modules
由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常復雜時,store 對象就有可能變得相當臃腫。
為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割。官方地址:https://vuex.vuejs.org/zh/guide/modules.html。
在store文件夾下,新建modules文件夾,在modules文件夾下面新建moduleA.js文件,內容如下:
const moduleA = { state: { count: 0 }, mutations: { increment (state,num) { // 這里的 `state` 對象是模塊的局部狀態 state.count += num } }, getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.age } }, actions:{ incrementIfOddOnRootSum ({ state, commit, rootState },param) { console.log(state,rootState,param) // state :局部state傳入值,如:{count:0},rootState:根元素state傳入值,param:外部傳入參 commit('increment',param.num) } } } export default moduleA
在store文件夾下index.js文件中,引入moduleA.js,內容如下:
import Vue from 'vue' // 引入vue import Vuex from 'vuex' // 引入vuex import state from './state' import * as getters from './getters' import * as mutations from './mutations' import * as actions from './actions' // import mutations from './mutations' // moudules import mouduleA from './modules/moduleA' // --------引入moduleA // 使用vuex Vue.use(Vuex) // 創建vuex實例 const store = new Vuex.Store({ state, getters, mutations, actions, modules:{ a:mouduleA // -----------實例化 } }) export default store
在頁面中使用moduleA,helloword.vue內容如下:
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>mouduleA--count:{{a.count}}</h2> // 使用mapState在頁面中顯示,也可直接寫 this.$store.state.a.count <h2>moduleA--sumWithRootCount:{{sumWithRootCount}}</h2> // 使用mapGetters <div><button @click="changeCount()">點我ModuleA的Count+某個數</button></div> </div> </template> <script> // 在單獨構建的版本中輔助函數為 Vuex.mapState import { mapState,mapGetters,mapMutations,mapActions } from 'vuex' // -------引入mapState,mapGetters,mapMutations,mapActions export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } }, computed:{ ...mapState(['a']), // 在computed中 --------------使用mapState ...mapGetters(['sumWithRootCount']), // computed中-----------使用mapGetters Count() { return this.$store.getters.doneTodosCount } }, methods:{ ...mapMutations(['increment']), // 在methods中-----------使用mapMutations ...mapActions(['incrementIfOddOnRootSum']), // 在methods中---------使用mapActions changeCount(){ // this.$store.commit('increment',11) // ------------一般方法 mutation 修改moduleA中state中的值 // this.$store.dispatch('incrementIfOddOnRootSum',{num:12}) // --------------一般方法 action 提交mutation // this.increment(12) // ----------使用mapMutations 修改moduleA中state的值 this.incrementIfOddOnRootSum({num:10}) // ------------使用mapActions 提交mutations } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
- store文件夾目錄
store文件夾下,經過上述的關於核心概念的介紹,新建了actions、getters、state、mutations和modules五個文件夾,除modules文件夾下面是moduleA.js之外,其他下面都是index.js文件。第一圖是store文件夾下的目錄,中圖是展開后,當然也可以根據自己的習慣管理文件。例如第三圖。
ok,上述代碼均經過本人測試,如果有問題,歡迎留言。