vue實踐04之vuex

nothing-everything發表於2018-04-12

vue實踐04之vuex

vuex是一個專門為vue.js設計的集中式狀態管理架構。我把它理解為在data中的屬性需要共享給其他vue元件使用的部分,就叫做狀態。簡單的說就是data中需要共用的屬性。比如:我們有幾個元件要顯示使用者名稱稱和使用者等級,或者顯示使用者的地理位置。如果我們不把這些屬性設定為狀態,那每個元件遇到後,都會到伺服器進行查詢計算,返回後再顯示。在中大型專案中會有很多共用的資料,所以尤大神給我們提供了vuex。

vuex把元件的共享狀態抽取出來,以一個全域性單例模式管理。在這種模式下,我們的元件樹構成了一個巨大的“檢視”,不管在樹的哪個位置,任何元件都能獲取狀態或者觸發行為。通過定義和隔離狀態管理中的各種概念並強制遵守一定的規則,我們的程式碼將會變得更結構化且易維護。

vuex示意圖
注:圖中虛線標註的區域即為vuex,可以看出vue components可以共享vuex。

適用場景

雖然 Vuex 可以幫助我們管理共享狀態,但也附帶了更多的概念和框架。這需要對短期和長期效益進行權衡。 如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗餘的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的global event bus參考連結 就足夠您所需了。但是,如果您需要構建一箇中大型單頁應用,您很可能會考慮如何更好地在元件外部管理狀態,Vuex 將會成為自然而然的選擇。

開始

每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。Vuex 和單純的全域性物件有以下兩點不同:

  1. Vuex 的狀態儲存是響應式的。當 Vue 元件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的元件也會相應地得到高效更新。

  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。

簡單例子

  1. 引入vuex npm install vuex --save 需要注意的是這裡一定要加上 --save,因為你這個包我們在生產環境中是要使用的。
  2. 新建一個vuex目錄,在目錄中新建store.js檔案
    state 對應定義的狀態值, mutations 內封裝對狀態值改變的函式。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
    state: {
        count: 1
    },
    mutations: {
        add(state) {
            state.count++;
        },
        reduce(state) {
            state.count--;
        }
    }
})
export default store
複製程式碼
  1. 在components目錄中新建count.vue檔案
<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
 
            }
        },
        store
        
    }
</script>
複製程式碼

注意上述程式碼中的兩個store.commit事件。

  • 通過提交 mutation 的方式,而非直接改變 store.state.count,是因為我們想要更明確地追蹤到狀態的變化。這個簡單的約定能夠讓你的意圖更加明顯,這樣你在閱讀程式碼的時候能更容易地解讀應用內部的狀態改變。此外,這樣也讓我們有機會去實現一些能記錄每次狀態改變,儲存狀態快照的除錯工具。有了它,我們甚至可以實現如時間穿梭般的除錯體驗。
  • 由於 store 中的狀態是響應式的,在元件中呼叫 store 中的狀態簡單到僅需要在計算屬性中返回即可。觸發變化也僅僅是在元件的 methods 中提交 mutation。
  1. 在路由檔案router/index.js檔案中新增count的路由
 {
            path: '/count',
            component: Count
        }
複製程式碼
  1. 訪問頁面http://127.0.0.1:8080/#/count
  2. 測試下vuex在不同component之間共享資料 在App.vue檔案中同樣也引用store,App.vue 程式碼如下:
<script>
import store from '@/vuex/store'
export default {
  name: "App",
   methods:{
    goback(){
      this.$router.go(-1);
    },
    goHome(){
      this.$router.push('/');
    }
  },
  // 把 store 物件提供給 “store” 選項,這可以把 store 的例項注入所有的子元件
  //(需呼叫 Vue.use(Vuex))
  store
};
</script>
複製程式碼

注意上述程式碼中引入了store選項,這個選項可以把store的例項注入所有的子元件。 在App.vue檔案template標籤裡增加顯示state的程式碼如下:

<h3>{{$store.state.count}}</h3>
複製程式碼

再次訪問count頁面,可以發現改變count元件的state值,App元件也會同步更新。

  1. 誤區-元件共享

筆者試著同時開啟兩個count頁面,天真以為改變一個頁面的計數值,另一個頁面也會自動變化,實際這是肯定不行的。vue就是一個單頁應用,多頁資料共享只能依賴與後端server的互動才能實現。vuex解決的時候單頁中的多component共享資料的問題。

vue元件獲得state

  1. 通過computed計算屬性直接賦值
computed:{
            computeCount(){
             return this.$store.state.count;
          }
        }
複製程式碼

顯示呼叫程式碼為<h4>{{computeCount}}</h4> 完整程式碼如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
         <h4>{{computeCount}}</h4>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
 
            }
        },
        computed:{
            computeCount(){
             return this.$store.state.count;
          }
        },
        store
        
    }
</script>
複製程式碼
  1. 通過mapState的物件來賦值
    當一個元件需要獲取多個狀態時候,將這些狀態都宣告為計算屬性會有些重複和冗餘。為了解決這個問題,我們可以使用 mapState 輔助函式幫助我們生成計算屬性,讓你少按幾次鍵。
  • 首先要用import引入mapState import {mapState} from 'vuex' -然後還在computed計算屬性裡寫如下程式碼
computed: mapState({
    // 箭頭函式可使程式碼更簡練
    computeCount: state => state.count,

    // 傳字串引數 'count' 等同於 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取區域性狀態,必須使用常規函式
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
複製程式碼

count2完整程式碼如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
         <h4>{{computeCount}}</h4>
         <h5>{{countAlias}}</h5>
         <h6>{{countPlusLocalState}}</h6>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    import {mapState} from 'vuex'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
                localCount:20
            }
        },
      computed: mapState({
    // 箭頭函式可使程式碼更簡練
    computeCount: state => state.count,

    // 傳字串引數 'count' 等同於 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取區域性狀態,必須使用常規函式
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  }),
        store
        
    }
</script>
複製程式碼
  1. 通過mapState的陣列來賦值
    當對映的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字串陣列。
computed: mapState([
  // 對映 this.count 為 store.state.count
  'count'
])
複製程式碼

完整程式碼如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <!--<h3>{{$store.state.count}}</h3>-->
        <h6>{{count}}</h6>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    import {mapState} from 'vuex'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
            }
        },
        computed: mapState([
            // 對映 this.count 為 store.state.count
          'count'
        ]),
        store
        
    }
</script>
複製程式碼

參考資料

技術胖老師vuex精講

vue官方文件

相關文章