vue項目--vuex狀態管理器


  本文取之官網和其他文章結合自己的理解用簡單化的語言表達。用於自己的筆記記錄,也希望能幫到其他小伙伴理解,學習更多的前端知識。

  • 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,上述代碼均經過本人測試,如果有問題,歡迎留言。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM