Vuex

2021-04-18 21:01:44

一、簡介
Vuex是服務於Vue.js應用程式的狀態管理模式。
vue狀態管理分為三部分
image.png

  • state,驅動應用的資料來源
  • view,以宣告方式將state對映到檢視
  • actions,響應在view上的使用者輸入導致的狀態變化

vuex的設計思想:在多個元件共用狀態時,將該共用狀態抽離出來以一個全域性單例模式管理。通過定義和隔離狀態管理中的各種概念,並通過強制規則維持檢視和狀態間的獨立性,讓程式碼變得更結構化且易維護。


二、安裝
npm安裝

npm install vuex --save

在模組化的打包系統中,通過Vue.use()安裝vuex
(當通過全域性script標籤參照Vuex時,不需要使用Vue.use()安裝)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

三、Store與State
每一個Vuex應用的核心就是store倉庫,store是一個容器,包含應用中大部分的state。
Vuex與單純的全域性物件的區別:
1、Vuex狀態儲存是響應式的,當vue元件從store中讀取state時,若state發生變化,則相應的元件也會相應地得到高效更新。
2、不能直接改變store中的state,改變的唯一途徑就是commit mutation。可以更方便跟蹤每一個狀態的變化。

使用new Vuex.Store()建立store

import Vue from 'vue'
import Vuex from 'vuex'

/*安裝Vuex*/
Vue.use(Vuex)

//建立一個store範例
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})
//建立一個元件範例,讀取state,修改state
const Counter = {
  template: `<div>
  <div class="btn" @click='add'>增加</div>
  {{count}}</div>`,
  computed: {
    //將state定義為計算屬性
    count() {
      // return store.state.count

      //在根範例vm中註冊store以後,所有子元件都能通過this.$store存取到state
      return this.$store.state.count
    }
  },
  methods: {
    add() {
      //修改state必須提交一個commit
      // store.commit('increment')

      //在根範例vm中註冊store以後,所有子元件都能通過this.$store存取到state
      this.$store.commit('increment')
    }
  }
}
//建立一個vue範例,提供一個建立好的store
let vm = new Vue({
  el: '#app',
  store, //把store物件提供給‘store’選項,可以把store的範例注入所有子元件中
  components: {
    Counter
  }
})

mapState獲取多個狀態
當一個元件需要獲取多個狀態時,將這些狀態都宣告為計算屬性會有些重複和冗餘,可使用mapState輔助函數幫助我們生成計算屬性。

//引入mapState
//import {mapState} from 'vuex'
const mapState = Vuex.mapState

const store = new Vuex.Store({
  state: {
    count: 0,
    isLogin: true,
    user: {
      username: 'Liane',
      id: '1',
      gender: 'female'
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

const Counter = {
  template: `<div>
        <div class="btn" @click='add'>增加</div>
        <div>{{count}}</div>
        <div>{{user.username}}</div>
        <div>{{user.id}}</div>
        </div>`,
  //使用mapState()輔助函數
  computed: mapState({
    // count() {
    //   return this.$store.state.count
    // }
    //count可簡化如下
    count: state => state.count,
    isLogin: state => state.isLogin,
    user: state => state.user
  }),
  methods: {
    add() {
      this.$store.commit('increment')
    }
  }
}
let vm = new Vue({
  el: '#app',
  store,
  components: {
    Counter
  }
})

當對映的計算屬性名稱與state的子節點名稱相同時,也可以給mapState()傳遞一個字串陣列

computed: mapState([
  'count',
  'isLogin', 
  'user'
])

通常我們使用擴充套件運運算元將需要用到的state通過mapState與元件的計算屬性混合

computed:{
  ...mapState([
    'count',
    'isLogin',
    'user'
  ])
}

四、Getter
當我們需要store中的某些state派生出一些狀態時,可以使用Getter,getter就像是store中的計算屬性一樣,會根據它的依賴被快取起來,依賴一旦發生改變,就會重新計算。

//定義一個store儲存代辦事項的狀態
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: 'study', done: true },
      { id: 2, text: 'exercise', done: true },
      { id: 3, text: 'painting', done: false }
    ]
  },
  getters: {
    //得到已完成的事項
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
    //得到已完成的事件個數
    doneTodosCount: (state, getters) => {
      return getters.doneTodos.length
    }
  }
})
const Counter = {
  template: `<div>
    <span>已完成{{doneTodosCount}}件代辦事項</span>
    <ul>
      <li v-for="item in doneTodos">
        {{item.text}}
      </li>
    </ul>
  </div>`,
  computed: {
    doneTodos() {
      return this.$store.getters.doneTodos
    },
    doneTodosCount() {
      return this.$store.getters.doneTodosCount
    }
  }
}
let vm = new Vue({
  el: '#app',
  store,
  components: {
    Counter
  }
})

mapGetter輔助函數

computed: {
  ...mapGetter([
    'doneTodos',
    'doneTodosCount'
  ])
}

若想將getter屬性另取名字,可使用物件形式

computed:{
  ...mapGetter({
    doneCount: 'doneTodosCount'
  })
}

五、Mutations
更改store中屬性的唯一方法就是commit一個mutation。
vuex中的mutation類似事件,每個mutation都有一個字串的事件型別(type)和一個回撥函數(handler)

const mapState = Vuex.mapState
const store = new Vuex.Store({
  state: {
    isLogin: false,
    user: {}
  },
  mutations: {
    setLogin(state, val) {
      state.isLogin = val
    },
    setUser(state, val) {
      state.user = val
    }
  }
})
const Counter = {
  data() {
    return {
      username: ''
    }
  },
  template:
        `<div>
          <div v-if='isLogin'>
            {{user.username}}已登入
            <a @click="signOut" href="javascript:;">退出登入</a>
          </div>
          <div v-else>
            <input v-model="username" placeholder="輸入使用者名稱">
            <div class="btn" @click="submit">登入</div>
          </div>
        </div>`,
  computed: {
    ...mapState(['isLogin', 'user'])
  },
  methods: {
    submit() {
      if (this.username) {
        this.$store.commit('setLogin', true)
        this.$store.commit('setUser', {
          id: 1,
          username: this.username
        })
      }
    },
    signOut() {
      this.$store.commit('setLogin', false)
      this.$store.commit('setUser', {})
    }
  }
}
let vm = new Vue({
  el: '#app',
  store,
  components: {
    Counter
  }
})

六、Actions
Action類似於mutation,區別:
1、Action提交的是mutation,而不是直接變更狀態。
2、Action可以包含任意非同步操作