Vuex 面試題:使用 vuex 的核心概念


Vuex 面試題

1. 什么是Vuex?

Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態, 並以相應的規則保證狀態以一種可預測的方式發生改變 簡單來說,就是用來集中管理數據;

2. Vuex解決了什么問題?

解決兩個問題:

  • 多個組件依賴於同一狀態時,對於多層嵌套的組件的傳參將會非常繁瑣,並且對於兄弟組件間的狀
    態傳遞無能為力。
  • 來自不同組件的行為需要變更同一狀態。以往采用父子組件直接引用或者通過事件來變更和同步狀
    態的多份拷貝。以上的這些模式非常脆弱,通常會導致無法維護的代碼。

3. 使用vuex的核心概念

1)store

vuex 中最關鍵的是 store 對象,這是 vuex 的核心。可以說,vuex 這個插件其實就是一個 store 對象,每
個 vue 應用僅且僅有一個 store 對象。

(1)創建store

const store = new Vuex.Store({...});

可見,store是Vuex.Store這個構造函數new出來的實例。在構造函數中可以傳一個對象參數。這個參數中可以包含5個對象:

  • state – 存放狀態
  • getters – state的計算屬性
  • mutations – 更改狀態的邏輯,同步操作
  • actions – 提交mutation,異步操作
  • mudules – 將store模塊化

關於store,需要先記住兩點:

  • store 中存儲的狀態是響應式的,當組件從store中讀取狀態時,如果store中的狀態發生了改變,
    那么相應的組件也會得到更新;
  • 不能直接改變store中的狀態。改變store中的狀態的唯一途徑是提交(commit)mutations。這樣使
    得我們可以方便地跟蹤每一個狀態的變化。

(2)完整的store的結構

const store = new Vuex.Store({
  state: {
    // 存放狀態
  },
  getters: {
    // state的計算屬性
  },
  mutations: {
    // 更改state中狀態的邏輯,同步操作
  },
  actions: {
    // 提交mutation,異步操作
  },
  // 如果將store分成一個個的模塊的話,則需要用到modules。
   //然后在每一個module中寫state, getters, mutations, actions等。
  modules: {
    a: moduleA,
    b: moduleB,
    // ...
  }
2) state

state上存放的,說的簡單一些就是變量,也就是所謂的狀態。沒有使用 state 的時候,我們都是直接在 data 中進行初始化的,但是有了 state 之后,我們就把 data 上的數據轉移到 state 上去了。另外有些狀態是組件私有的狀態,稱為組件的局部狀態,我們不需要把這部分狀態放在 store 中去。

(1)如何在組件中獲取 vuex 狀態

由於 vuex 的狀態是響應式的,所以從 store 中讀取狀態的的方法是在組件的計算屬性中返回某個狀態。

import store from 'store';
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      // 獲取store中的狀態
      return store.state.count;
    }
  }
}

這樣,組件中的狀態就與 store 中的狀態關聯起來了。每當 store.state.count 發生變化時,都會重新求取計算屬性,從而更新 DOM。

然而,每個組件中都需要反復倒入 store。可以將 store 注入到 vue 實例對象中去,這樣每一個子組件中都可以直接獲取 store 中的狀態,而不需要反復的倒入 store 了。

const app = new Vue({
  el: '#app',
  // 把 store 對象注入到了
  store,
  components: { Counter },
  template: `
        <div>
            <counter></counter>
        </div>
    `
});

這樣可以在子組件中使用 this.$store.state.count 訪問到 state 里面的 count 這個狀態

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      // 獲取store中的狀態
      return this.$store.state.count;
    }
  }
}

(2) mapState

當一個組件獲取多種狀態的時候,則在計算屬性中要寫多個函數。為了方便,可以使用 mapState 輔助函數來幫我們生成計算屬性。

import { mapState } from  'vuex';
export default {
  // ...
  data (){
    localState: 1
  }
  computed: mapState({
    // 此處的state即為store里面的state
    count: state => state.count,
    // 當計算屬性的名稱與state的狀態名稱一樣時,可以省寫
    // 映射 this.count1 為 store.state.count1
    count1,
    //'count'等同於 ‘state => state.count’
    countAlias: 'count',
    countPlus (state){
    // 使用普通函數是為了保證this指向組件對象
      return state.count + this.localState;
    }
  })
}
//上面是通過mapState的對象來賦值的,還可以通過mapState的數組來賦值
computed: mapState(['count']);
//這種方式很簡潔,但是組件中的state的名稱就跟store中映射過來的同名

對象擴展運算符

mapState 函數返回的是一個對象,為了將它里面的計算屬性與組件本身的局部計算屬性組合起來,需要用到對象擴展運算符。

computed: {
  localState () { 
    ...mapState ({
    })
  }
}

這樣,mapState 中的計算屬性就與 localState 計算屬性混合一起了。

3)getters

有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數。此時可以用到 getters,getters 可以看作是 store 的計算屬性,其參數為 state。

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

(1)獲取getters里面的狀態,方法一

store.getters.doneTodos //  [{ id: 1, text: 'reading', done: true }]
//在組件中,則要寫在計算屬性中,
computed: {
  doneTodos () {
    return this.$store.getters.doneTodos;
  }
}

(2) 使用mapGetters獲取getters里面的狀態:方法二

import {mapState, mapGetters} from 'vuex';
computed: {
    ...mapState(['increment']),
    ...mapGetters(['doneTodos'])
}
4)mutations

mutations 里面是如何更改 state 中狀態的邏輯。更改 Vuex 中的 state 的唯一方法是,提交 mutation,即 store.commit(‘increment’) 。

(1) 提交載荷 (payload)

可以向 commit 傳入額外的參數,即 mutation 的載荷。

mutations: {
  increment(state, n){
    state.count += n;
  }
}
store.commit('increment', 10);

payload 還可以是一個對象。

mutations: {
  increment(state, payload)
  state.count += payload.amount;
}
store.commit('increment', {amount: 10});

還可以使用 type 屬性來提交 mutation。

store.commit({
  type: 'increment',
  amount: 10
});
// mutations保持不變
mutations: {
  increment(state, payload){
    state.count += payload.amount;
  }
}

注意:mutation 必須是同步函數,不能是異步的,這是為了調試的方便。

(2)在組件中提交 mutations

那么 mutation 應該在哪里提交呢? 因為js是基於事件驅動的,所以改變狀態的邏輯肯定是由事件來驅動的,所以 store.commit(‘increment’) 是在組件的 methods 中來執行的。

方法1: 在組件的 methods 中提交

methods: {
  increment(){
    this.$store.commit('increment');
  }
}

方法2: 使用 mapMutaions

用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用。

import { mapMutaions } from 'vuex';
export default {
  // ...
  methods: {
    ...mapMutaions([
    'increment' // 映射 this.increment() 為 this.$store.commit('increment')
  ]),
  ...mapMutaions([
      add: 'increment' // 映射 this.add() 為 this.$store.commit('increment')
    ])
    }
}
// 因為mutation相當於一個method,所以在組件中,可以這樣來使用
<button @click="increment">+</button>
5)actions

因為 mutations 中只能是同步操作,但是在實際的項目中,會有異步操作,那么 actions 就是為了異步操作而設置的。這樣,就變成了在 action 中去提交 mutation,然后在組件的 methods 中去提交 action。只是提交 actions的時候使用的是 dispatch 函數,而 mutations 則是用 commit 函數。

(1)一個簡單的 action

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state){
      state.count++;
    }
  },
  actions: {
    increment(context){
      context.commit('increment');
    }
    /* 可以用參數結構的方法來寫action
     increment({commit}){
     commit('increment');
     }
     */
  }
});
// action函數接受一個context參數,這個context具有與store實例相同的方法和屬性。
// 分發action
store.dispatch('increment');

action 同樣支持 payload 和對象方式來分發,格式跟 commit 是一樣的,不再贅述。

(2)在組件中分發 action

方法1: 在組件的 methods 中,使用 this.$store.dispatch(‘increment’)
方法2: 使用 mapActions,跟 mapMutations 是類似的。

import { mapActions } from 'vuex'
export default {
  // ...
  methods: {
    ...mapActions([
    'increment' // 映射 this.increment() 為 this.$store.dispatch('increment')]),
    ...mapActions({
      add: 'increment' // 映射 this.add() 為 this.$store.dispatch('increment')
    })
  }
}
// 同樣在組件中,可以這樣來使用
<button @click="increment">+</button>

(3)組合 actions

因為 action 是異步的,那么我們需要知道這個異步函數什么時候結束,以及等到其執行后,會利用某個 action 的結果。這個可以使用 promise 來實現。在一個 action 中返回一個 promise,然后使用 then() 回調函數來處理這個 action 返回的結果。

actions:{
  actionA({commit}){
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation');
        resolve();
      },1000);
    })
  }
}
// 這樣就可以操作actionA返回的結果了
store.dispatch('actionA').then(() => {
  // dosomething ...
});
// 也可以在另一個action中使用actionA的結果
actions: {
  // ...
  actionB({ dispatch, commit }){
    return dispatch('actionA').then(() => {
      commit('someOtherMutation');
    })
  }
}

更多面試題:119頁Vue面試題總結可【點擊此處免費領取!


免責聲明!

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



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