Vue.js簡單的狀態管理和 Vuex的幾個核心概念使用。


因爲狀態零散地分佈在許多組件和組件之間的交互中,大型應用複雜度也常常逐漸增加。html

  • 若是多層組件嵌套使用,傳遞prop,和事件emit。都很不方便。
  • 不方便對數據的修改進行歷史記錄。影響後續的調試!

爲了解決這個問題,Vue 提供 vuexvue

vuex 甚至集成到 vue-devtools,無需配置便可進行時光旅行調試git

 


 


Vuex 

 

前置知識:理解什麼是component!

組件就是函數。編程就是經過組織小的函數們來解決問題!相似組件!github

因而問題就成爲:如何傳遞arguments, 組件是怎樣和它的環境互動的?vuex

就像parameters和返回函數中的值。npm

 

在Vue中咱們使用props來傳遞data到組件和組件事件,來和周邊環境交互。編程

prop例子api

message prop從父組件傳遞到子組件,經過一個template。數組

//childrenComponent
{
  props: ['message'],
  created(){
    console.log(message)  
  },
  template: '<div>{{ message }}</div>'
}

// Parent Component
{
  template: `<child-component message="'hello'"></child-component>`,
  components: { childrenComponent }
}

 

event例子(見連接) 主要使用$emit方法和v-on緩存

 

一個問題: 

這是很好的移動數據的辦法。只是有一個組件的問題:當嵌套3+層時,交互成了困難。

你不得不傳遞數據從組件A到B,而後從B到C,等等。 反向events, C emits to B, B to A等等。

 

因而使用state進行狀態管理出現了,但這有缺陷,因此出現了Vue, 日後看👇:

 

簡單狀態管理

常常被忽略的是,Vue 應用中原始數據對象的實際來源 。

當訪問數據對象時,一個 Vue 實例只是簡單的代理訪問。

因此,若是你有一處須要被多個實例間共享的狀態,能夠簡單地經過維護一份數據來實現共享:

const sourceOfTruth = {}

const vmA = new Vue({ data: sourceOfTruth }) const vmB = new Vue({ data: sourceOfTruth })

 子組件的實例能夠經過this.$root.$data來訪問sourceOfTruth.

 

問題出現:任何對數據的改變,不會留下變動過的記錄。這對調試是一個噩夢!!

所以可使用store模式:

var store = {
  debug: true, state: { message: 'Hello' }, setMessageAction(newVaule) { if (this.debug) console.log('setMessageAction triggered with', newValue) this.state.message = newValue }, clearMessageAction() { if (this.debug) console.log('clearMessageAction triggered') this.state.message = '' } }

 store中的state的改變,都放置在store自身的函數去管理!這就是集中方式的狀態管理!

 當錯誤出現時, 就有了log來記錄bug出現以前發生了什麼。

 

 此外,每一個實例/組件,能夠有本身的私有狀態:

var vma = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

var vmb = new Vue({ data: { privateState: {}, sharedState: store.state } })

 

接下來的延伸約定:

組件不該該直接修改屬於store實例的state ,而應該執行store實例內的action函數來dispatch事件通知store去改變。

這樣約定的好處:

能夠記錄全部store中發生的state的改變,同時實現能作到記錄變動mutation, 保存狀態快照,歷史回滾/時光旅行的先進的調試工具:vuex.


 

 

Vuex

Vuex用於集中管理state。或者說是集中式的狀態管理Vue依賴庫。 Vuex所作的不是在組件內協調state的數據(coordinate state's data in components), 而是,在一個巨大的state object內,協調coordinate它們(data)。 任何組件須要一個portion of the state, 就傳遞它們。

這個state或大或小由程序決定,不過最開始就是一個空對象{ }。

 

安裝

npm install vuex  
//或者進入ui,在網頁上手動添加。

//而後配置到entry point,或自建一個store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

//若是自建store.js, 加上:
export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {}
});

 

 

Vuex是一個全局對象,還有2個額外的功能:

  1. Vuex的state儲存是響應式的。
  2. 不能夠直接改變store中的state。須要使用(commit)mutation.

簡單計數:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

store.commit('increment')
store.commit('increment')

store.state.count  //輸出2

 

 


 

核心概念:

  • State: 保存組件的共享的狀態。
  • Getter:用於state的延伸,就是computed計算屬性的功能。
  • Mutation: 惟一能夠改變store的狀態的方法:(commit)mutation
  • Action: 只能提交Mutation,能夠包括async操做。
  • Module: 解決單一狀態樹,致使對象過於龐大的問題。將store分割爲多個module模塊。至關因而嵌套子模塊。

State

單一狀態樹,一個對象包含所有應用層級狀態。 每一個應用只有一個store實例。

Vuex經過store選項,把狀態從根組件‘注入’到子組件中。


//
輔助函數mapState的用法 //在子組件中,無需每一個state都聲明computed屬性,使用輔助函數,幫助咱們生成計算屬性! //計算屬性? //若是想要在子組件獲得store實例中的狀態,最簡單的方法就是在計算屬性中返回某個狀態
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count } } }

 

store.state.count變化的時候,會重新求取計算屬性, 並觸發更新相關的DOM。

可是,這種模式有個缺陷!!❌!! 

即簡單使用store實例,集中管理的缺陷

vue使用的是模塊化的構建方法,每一個須要用到state的組件都會頻繁的導入store.state。過於頻繁下降效能。

Vuex經過store選項,提供了一種機制將state從根組件‘注入’到每一個子組件中(調用Vue.use(Vuex))

const app = new Vue({
  el: '#app',
  // 把 store 對象提供給 「store」 選項,這能夠把 store 的實例注入全部的子組件
  store,

 

子組件能夠經過this.$store來訪問state。

例子:

根實例vm, 子組件Counter。

vm中註冊了store選項,得到了store對象,而後把store實例注入到Counter子組件內。

Counter使用this.$store.state.count訪問到store實例。

(個人理解是每一個子組件,都有一個store實例,這樣子組件內部能夠反覆調用這個實例狀態,無需再從store對象那裏取數據)

Counter還可使用this.$store.commit("increment")來修改store對象的state。

(若是是修改state,則調用store對象的action修改它的state)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

const  Counter = {
  template: `<div>count: {{ count }}</div>`,
  computed: {
    count() {
      return this.$store.state.count
    }
  }
}

var vm = new Vue({
  el: "#app",
 store,
  components: { Counter },
})

 

mapState 輔助函數

當一個組件須要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘。爲了解決這個問題,咱們可使用 mapState 輔助函數幫助咱們生成計算屬性,讓你少按幾回鍵:

import { mapState} from 'vuex'

export default{
  computed: mapState({
     count: state => state.count
     countAlias: 'count'

     //爲了可以使用 `this` 獲取局部狀態,必須使用常規函數
     countPlusLocalState(state) {
        return state.count + this.localCount
     }

 })
}

 

若是映射的計算屬性的名字和state的子節點名字相同時,能夠給mapState傳遞一個string數組

We can also pass a string array to mapState when the name of a mapped computed property is the same as a state sub tree name.

computed: mapState([
  // 映射 this.count 爲 store.state.count
  // this是指子組件實例
  'count'
])

 

 

對象展開符號...

使用..., 簡化了上面的代碼

computed: {
  localComputed() { /* ... */},
  ...mapState({
    // ...
  })
}

 

 

 


 

Getter

一個簡單的演示:mycode pen:https://codepen.io/chentianwei411/pen/JmyeGq

 

針對state的派生的狀態的操做:

(store的計算屬性),getter返回值根據它的依賴被緩存起來,只當它的依賴值改變了纔會被從新計算。

Getter接受state爲第一個參數

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

 

經過屬性訪問:

store.getters對象中的函數能夠訪問:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

 

Getter能夠接受其餘getter做爲第二個參數:個人理解,就是store.getters對象被看成第二個參數

const mystore = new Vuex.Store({
  //略。。
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
 doneTodosCount: (state, getters) => { return getters.doneTodos.length }
    
  }
})

 

咱們能夠在任何組件中使用getters中的函數:

const  Counter = {
  template: `<div>count: {{ count }}</div>`,
  computed: {
    count() {
      return this.$store.getters.doneTodosCount
    }
  }
}

var vm = new Vue({
  el: "#app",
  store: mystore
  components: { Counter },
})

 

經過方法訪問:返回的是一個函數 (state) => {  return function(){} }

  getters: {
    //..略
    getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
    }
  },

mystore.getters.getTodoById(2)   
//->返回todo對象一個。{ id: 2, text: '...', done: false }

 


 

Mutation

Mycodepen:2個演示:

https://codepen.io/chentianwei411/pen/PyKyNr

mutations屬性中的函數能夠接受2個參數,

  1. 第一個參數是state.
  2. 第2個參數最好是一個包含record的對象
  mutations: {//添加參數payload,約定它是對象。
    incrementPayload (state, payload) {
      state.count += payload.amount
    }
  }

 

 

Mutation必須是同步函數,不能說異步函數,由於當mutation觸發時,回調函數沒能被調用,那麼就不能記錄追蹤回調函數中進行的狀態的改變。

使用action處理異步操做:


 

 

Action


Action 相似於 mutation,不一樣在於:

    • Action 提交的是 mutation,而不是直接變動狀態。
    • Action 能夠包含任意異步操做
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
 actions: { increment (context) { setTimeout( () => { context.commit('increment') }, 1000) } }
}) 

 

 

Action 函數接受一個與 store 實例具備相同方法和屬性的 context 對象,所以你能夠調用 :

  • context.commit 提交一個 mutation。
  • context.state
  • context.getters

當咱們在以後介紹到 Modules 時,你就知道 context 對象爲何不是 store 實例自己了。

 

dispatch Action

不受必須同步執行的限制。

store.dispatch('increment')

 

 

一樣可使用載荷,對象的方式分發:

個人理解:action中異步調用mutations中的函數。actions和mutations中的函數名字一一對應。

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
   //...略...
    incrementPayload (state, payload) {
      state.count += payload.amount 
    }
  },
  actions: {
   //...略... incrementPayload (context, payload) {
      setTimeout( () => {
        context.commit('incrementPayload', payload)
      }, 1000)
    }
  }
}) 

 

 

在組件中分發Action(未看)

組合 Action(未看)

 


 

項目結構:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 咱們組裝模塊並導出 store 的地方
    ├── actions.js        # 根級別的 action
    ├── mutations.js      # 根級別的 mutation
    └── modules
        ├── cart.js       # 購物車模塊
        └── products.js   # 產品模塊


 

 

插件

Vuex.Store的實例方法:

subscribe(handler: Function): Function

hander會在每一個mutation完成後調用,接收mutation和通過mutation後的state做爲參數:

相關文章
相關標籤/搜索