Vuex

HuangQiaoqi發表於2024-05-04

【一】瞭解Vuex

【1】想象一個場景

如果你的專案裡有很多頁面(元件/檢視),頁面之間存在多級的巢狀關係,此時,這些頁面假如都需要共享一個狀態的時候,此時就會產生以下兩個問題:

  • 多個檢視依賴同一個狀態
  • 來自不同檢視的行為需要變更同一個狀態

【2】解決方案

​ 對於第一個問題,假如是多級巢狀關係,你可以使用父子元件傳參進行解決,雖有些麻煩,但好在可以解決;對於兄弟元件或者關係更復雜元件之間,就很難辦了,雖然可以透過各種各樣的辦法解決,可實在很不優雅,而且等專案做大了,程式碼就會變成屎山,實在令人心煩。

​ 對於第二個問題,你可以透過父子元件直接引用,或者透過事件來變更或者同步狀態的多份複製,這種模式很脆弱,往往使得程式碼難以維護,而且同樣會讓程式碼變成屎山。

【3】換一種思路

​ 把各個元件都需要依賴的同一個狀態抽取出來,在全域性使用單例模式進行管理

​ 在這種模式下,任何元件都可以直接訪問到這個狀態,當狀態發生改變時,所有元件都發生更新

【4】vuex的誕生

​ 這就是 Vuex 背後的基本思想,借鑑了 Flux、Redux。與其他模式不同的是,Vuex 是專門為 Vue 設計的狀態管理庫,以利用 Vue.js 的細粒度資料響應機制來進行高效的狀態更新。

【5】什麼時候該用vuex

​ 這個問題因人而異,如果你不需要開發大型的單頁應用,此時你完全沒有必要使用vuex,比如你的頁面就兩三個,使用vuex後增加的檔案比你現在的頁面還要多,那就沒這個必要了。

​ 假如你的專案達到了中大型應用的規模,此時您很可能會考慮如何更好地在元件外部管理狀態,Vuex 將會成為自然而然的選擇。

【二】安裝Vuex

進入專案,在命令列輸入安裝指令

npm install vuex --save

然後配置 vuex,使其工作起來:在src路徑下建立store資料夾,然後建立index.js檔案,檔案內容如下:

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

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    // 定義一個name,以供全域性使用
    name: '張三',
    // 定義一個number,以供全域性使用
    number: 0,
    // 定義一個list,以供全域性使用
    list: [
      { id: 1, name: '111' },
      { id: 2, name: '222' },
      { id: 3, name: '333' },
    ],
  },
});

export default store;


修改main.js

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

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        // 定義一個name,以供全域性使用
        name: 'hqq'
    },
})

最後修改APP.vue

export default {
  name: 'HomeView',
  data() {
    return {}
  },
  methods: {},
  created() {
    console.log('全域性', this.$store.state.name)
  },


}
</script>

此時,啟動專案,開啟控制檯就會發現輸出了在store裡面定義的name值

  • 官方建議我們以上操作this.$store.state.XXX最好放在計算屬性中,當然,我也建議你這麼使用,這樣可以讓你的程式碼看起來更優雅一些,就像這樣:
export default {
  name: 'HomeView',
  data() {
    return {}
  },
  methods: {},
  computed: {
    getName() {
      return this.$store.state.name
    }
  },
  created() {
    console.log('計算屬性', this.getName)
  },


}
</script>

此時就可以得到在控制檯得到一樣的效果

【三】修飾器Getter

【1】場景

getter時一個讀取操作的修飾利器,

假設一個場景,

  • 你已經將store的name全部都展示到頁面上了,
  • 此時產品經理說把所有的name前面都加上hello
  • 這是如果沒有別的辦法就只能在每個頁面上使用this.$store.state.name獲取到值之後,進行遍歷,前面追加"hello"。

這樣做很不好,原因如下

  • 程式碼冗餘
  • 如果下次有其他需求,要把hello改成hi,又要進行很大的修改

【2】使用

所以這時候getter就登場了,它可以從源頭解決問題,它可以把state裡的資料放到一個方法裡面,得到返回值,如果有修改需求,只需要修改這個方法的返回值即可

首先,在store物件中增加getters屬性

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

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        // 定義一個name,以供全域性使用
        name: 'hqq'
    },
    getters: {
        getMessage(state) {
            // 獲取修飾後的name,第一個引數為必要引數,得到的就是state物件
            return `大帥哥${state.name}`
        }
    }
})

在元件中使用

export default {
  name: 'HomeView',
  methods: {},
  created() {
    console.log(this.$store.getters.getMessage)
  },
}

【四】修改值Mutation

說到修改,大多數人會想到直接得到值然後=賦值修改

this.$store.state.XXX = XXX;

這樣做確實可以達到效果,但是不好,vuex開放了專門的介面提供給我們修改

修改store/index.js

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

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        // 定義一個name,以供全域性使用
        name: 'hqq'
    },
    getters: {
        getMessage(state) {
            // 獲取修飾後的name,第一個引數為必要引數
            return `大帥哥${state.name}`
        }
    },
    // 改值用的
    mutations: {
        // 第一個引數是state物件,第二個引數放要修改的值
        setName(state, name) {
            state.name = name
        }
    },
})

修改APP.vue

export default {
  name: 'HomeView',
  created() {
    this.$store.commit('setName', 'green')
  },
}

【五】非同步操作Actions

​ Actions存在的意義是假設你在修改state的時候有非同步操作,vuex作者不希望你將非同步操作放在Mutations中,所以就給你設定了一個區域,讓你放非同步操作,這就是Actions

修改store/index.js

const store = new Vuex.Store({
  state: {
    name: '張三',
    number: 0,
  },
  mutations: {
    setNumberIsWhat(state, payload) {
      state.number = payload.number;
    },
  },
  actions: {
    // 增加actions屬性
    setNum(content) {
      // 增加setNum方法,預設第一個引數是content,其值是複製的一份store
      return new Promise(resolve => {
        // 我們模擬一個非同步操作,1秒後修改number為888
        setTimeout(() => {
          content.commit('setNumberIsWhat', { number: 888 });
          resolve();
        }, 1000);
      });
    },
  },
});

修改App.vue

async mounted() {
  console.log(`舊值:${this.$store.state.number}`);
  await this.$store.dispatch('setNum');
  console.log(`新值:${this.$store.state.number}`);
},

相關文章