Vuex的介紹和使用

vuex是什么?

  • Vuex 是一個(gè)專為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理庫,可以用于各種復(fù)雜組件之間的通信婚脱,屬于vue全家桶相關(guān)的生態(tài)工具鏈

什么情況下需要使用vuex?

  • 如果開發(fā)過程中存在各種組件或者很多復(fù)雜的組件勺像,且各種組件之間需要進(jìn)行相互通信(個(gè)人認(rèn)為即如果把某一個(gè)頁面拆成各種小組件時(shí)障贸,便需要考慮使用vuex進(jìn)行通訊,提升組件通信間的開發(fā)效率)

如果不使用Vuex通信還能使用什么吟宦?

  • 父子組件通訊
  • 兄弟組件通信
  • 隔代組件通訊

vuex的工作流程

  • 圖示如下:
image.png
  • 工作流程文字描述如下:
  1. vue組件里面篮洁,通過dispatch來觸發(fā)actions提交修改數(shù)據(jù)的操作。
  2. 然后再通過actionscommit來觸發(fā)mutations來修改數(shù)據(jù)殃姓。
  3. mutations接收到commit的請求袁波,就會(huì)自動(dòng)通過Mutate來修改state里面的值。
  4. 最后由store觸發(fā)每一個(gè)調(diào)用它的組件的更新

如何使用Vuex

  1. 使用 vue-cli 生成項(xiàng)目時(shí)蜗侈,可以選擇集成 vuex 到項(xiàng)目中篷牌。此時(shí), vue-cli 會(huì)自動(dòng)安裝 vuex 踏幻,并在 src 文件夾下生成store文件下的 store.js 完成 vuex 的引入和配置枷颊。

  2. 手動(dòng)引入安裝

    npm install vuex 
    //或者
    yarn add vuex
    
  3. (備注)如果是vue-cli創(chuàng)建的項(xiàng)目,且在最開始集成了vuex則不需要進(jìn)行如下配置该面,否則請參考如下配置

  4. image.png
  1. image.png

vuex的五大核心要素

Start

  1. state 表示狀態(tài)夭苗,類似于 vue 中的data,用來存放狀態(tài)值,里面存放的數(shù)據(jù)是響應(yīng)式的
// 代碼參考

// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    text: "hello word",
  },
  getters: {},
  mutations: {},
  actions: {},
  modules: {},
});

//某組件代碼(其他頁面訪問方式相同)
<template>
  <div>
    <h2>我是某組件代碼</h2>
    //頁面通過 $store.state.text 可以獲取相關(guān)值
    <h3>這是vuex中的數(shù)據(jù):{{ $store.state.text }}</h3>
  </div>
</template>

<script>
export default {
  name: "allHome",
  methods: {
    getVuex() {
        //這里通過 this.$store.state.text 可以獲取相關(guān)值
      console.log("這是vuex中的數(shù)據(jù):" + this.$store.state.text);
    },
  },
  mounted() {
    this.getVuex();
  },
};
</script>

<style></style>

Getter

  1. 類似于 Vue 中的 計(jì)算屬性(可以認(rèn)為是 store 的計(jì)算屬性)吆倦,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來听诸,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算
// 代碼參考  ... 表示省略這里的代碼(因?yàn)檫@里代碼是相同的),省略部分請參考對照上方代碼

// store.js
....
  state: {
    students: [
      { id: 0, name: "張三", age: 25 },
      { id: 1, name: "李四", age: 30 },
      { id: 2, name: "王二", age: 19 },
      { id: 3, name: "麻子", age: 18 },
    ],
  },
  getters: {
    greaterAgeCount: (state) => {
      return state.students.filter((s) => s.age > 20).length;
    },
  },
 //也可以采取下面這種寫法蚕泽,不過這種寫法需要通過方法訪問
  // getters: {
  //   greaterAgeCount: (state) => {
  //     return function (age) {
  //       return state.students.filter((s) => s.age > age).length;
  //     };
  //   },
  // },
....
});

//某組件代碼(其他頁面訪問方式相同)
<template>
  <div>
    <h2>我是某組件代碼</h2>
    <h3>這是vuex中的 getter 返回值的數(shù)據(jù):{{ stundetConut }}</h3>
    <!-- 也可以采用方法的方式訪問晌梨,參考如下 -->
    <!-- <h3>
      我是屬性寫法的得到的getter 返回值 {{ $store.getters.greaterAgeCount(10) }}
    </h3> -->
  </div>
</template>

<script>
export default {
  name: "allHome",
  computed: {
    stundetConut() {
      return this.$store.getters.greaterAgeCount;
    },
  },
  // 也可以采用方法的方式訪問桥嗤,參考如下
  // methods: {
  //   gevue() {
  //     console.log(this.$store.getters.greaterAgeCount(10));
  //   },
  // },
  // mounted() {
  //   this.gevue();
  // },
};
</script>

<style></style>


Mutaion

  1. (只能處理同步)可以理解為官方規(guī)定修改Vuexstore 中的狀態(tài)的唯一方法,類似于事件仔蝌,如果要修改state的值泛领,需要通過它進(jìn)行,才能更好的使用 devtools追蹤狀態(tài)變化敛惊。當(dāng)然你也可以不在這里修改渊鞋,因?yàn)檫@里只是官方推薦的規(guī)范和vuex架構(gòu)設(shè)計(jì)的概念。
// 代碼參考  ... 表示省略這里的代碼(因?yàn)檫@里代碼是相同的)瞧挤,省略部分請參考對照上方代碼

// store.js
....
  mutations: {
    //state 表示數(shù)據(jù) value 表示傳遞過來的值
    addText(state锡宋,value) {
      state.text = state.text + value;
    },
  },
....
});

//某組件代碼(其他頁面訪問方式相同)
<template>
  <div>
    <h2>我是某組件代碼</h2>
    <h3>這是vuex中的數(shù)據(jù):{{ $store.state.text }}</h3>
    <button @click="changeVue('good')">點(diǎn)擊變化vuex值</button>
  </div>
</template>

<script>
export default {
  name: "allHome",
  methods: {
    getVuex() {
      console.log("這是vuex中的數(shù)據(jù):" + this.$store.state.text);
    },
    changeVue(text) {
      /**
       * 這里通過 this.$store.commit("a",b)可以觸發(fā)對應(yīng)事件 a 表示需要vuex中 mutations定義的 事件名,b表示需要傳遞給vuex的值特恬,如果是多個(gè)值請使用對象表示
       * text{
            name:"aaa"
            age:18 
          }
       */
      this.$store.commit("addText", text);
    },
  },
  mounted() {
    this.getVuex();
  },
};
</script>

<style></style>

Action

  1. (可以處理異步)执俩,這里可以理解為action是用來分發(fā)將要進(jìn)行的操作事件的,它將要做的事情癌刽,提交給mutation役首,再由mutations進(jìn)行修改。
  2. 當(dāng)然這里也可以直接修改state的值(不過在vue2vuex中官方不是很推薦显拜,vue3中的推薦pinia狀態(tài)管理庫已經(jīng)是使用這個(gè)修改State值了)
// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
       context.commit('increment')
    }
  }
})

//某組件代碼(其他頁面訪問方式相同)
<template>
  <div>
    <h2>我是某組件代碼</h2>
    <div>{{ $store.state.count }}</div>
    <!-- 這里使用 $store.dispatch('a') 觸發(fā) mutation 中的事件衡奥,再由 mutation 進(jìn)行修改,其中 a 表示mutation 中的事件名字远荠,  -->
    <button @click="$store.dispatch('increment')">修改count</button>
  </div>
</template>

<script>
export default {
  name: "allHome",
};
</script>

<style></style>

Module

  1. 用于在存在很多狀態(tài)值的時(shí)候矮固,進(jìn)行模塊劃分的,特別是在大型應(yīng)用中矮台,如果使用單一狀態(tài)樹(只寫一個(gè)文件)乏屯,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對象∈莺眨可能會(huì)導(dǎo)致代碼不好維護(hù)和store 對象臃腫辰晕。這時(shí)我們可以將 store 分割為模塊(module),每個(gè)模塊擁有自己的 state 确虱、 getters 含友、mutationsactions 校辩、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割窘问。
// 主狀態(tài)store.js

import Vuex from 'vuex';
import moduleA from './modules/modulesA';
import moduleB from './modules/modulesB';

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {
    moduleA
    moduleB,
  },
});

// 拆分的模塊狀態(tài)一 moduleA.js

export default {
  //這里使用的命名空間,避免內(nèi)部如果存在名字一樣方法的時(shí)候觸發(fā)其它模塊的同名方法
  //如果使用的命名空間后宜咒,如果要觸發(fā)模塊中的的 mutation 就需要加上路徑才能調(diào)用  store.commit('moduleA/print');
  namespaced: true,
  state: () => ({
    a1: 'aaa',
  }),
  mutations: {
    print(state) {
      console.log(state.a1);
    },
  },
  actions: {},
  getters: {},
};

// 拆分的模塊狀態(tài)一 moduleB.js

export default {
  //這里使用的命名空間惠赫,避免內(nèi)部如果存在名字一樣方法的時(shí)候觸發(fā)其它模塊的同名方法
  //如果使用的命名空間后,如果要觸發(fā)模塊中的的 mutation 就需要加上路徑才能調(diào)用  store.commit('moduleA/print');
  namespaced: true,
  state: () => ({
    a2: 'bbb',
  }),
  mutations: {
    print(state) {
      console.log(state.a2);
    },
  },
  actions: {},
  getters: {},
};

vuex輔助函數(shù)

  • 主要是為了節(jié)約代碼書寫
  • (備注)輔助函數(shù)具體是否使用故黑,可以根據(jù)自己需要進(jìn)行選擇

mapState

// 原寫法

<template>
  <div>
    <span>{{ customerName }}</span>
  </div>
</template>
<script>
export default {
  computed: {
    customerName() {
      return this.$store.state.customerName;
    }
  }
}
</script>

// 使用 mapState 輔助函數(shù)寫法
<template>
  <div>
    <span>{{ customerName }}</span>
  </div>
</template>
<script>
import { mapState } from "vuex";
export default {
  computed: mapState({
    // 箭頭函數(shù)可使代碼更簡練
    count: (state) => state.count,

    // 傳字符串參數(shù) 'count' 等同于 `state => state.count`
    countAlias: "count",

    // 為了能夠使用 `this` 獲取局部狀態(tài)儿咱,必須使用常規(guī)函數(shù)
    countPlusLocalState(state) {
      return state.count + this.localCount;
    },
  }),
  // 個(gè)人推薦這種寫法庭砍,給 mapState 傳一個(gè)字符串?dāng)?shù)組
  
  // 當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí),我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組混埠。
  // 映射 this.count 為 store.state.count
  //   computed: mapState([
  //   // 映射 this.count 為 store.state.count
  //   'count'
  // ])
}
</script>

mapGetters

// 原寫法
<template>
  <div>
    <span>Shopping Cart ({{ cartItemCount }} items)</span>
  </div>
</template>
<script>
export default {
  computed: {
    cartItemCount() {
      return this.$store.getters.cartItemCount;
    }
  }
}
</script>

// 使用 mapGetters 輔助函數(shù)寫法

<template>
  <div>
    <span>Shopping Cart ({{ cartItemCount }} items)</span>
  </div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
  computed: {
    ...mapGetters(['cartItemCount'])
  }
}
</script>

mapMutations

// 原寫法
<template>
  <div>
    <p>{{ customerName }}</p>
    <input type="text" @input="updateName" :value="customerName" />
  </div>
</template>
<script>
import { mapState } from "vuex";
export default {
  name: "Example",
  computed: {
    ...mapState(['customerName'])
  },
  methods: {
    updateName(event) {
      this.$store.commit('setCustomerName', event.target.value);
    }
  }
}
</script>

// 使用 mapMutations 輔助函數(shù)寫法

import { mapState, mapMutations } from 'vuex';
export default {
  name: "Example",
  computed: {
    ...mapState(['customerName'])
  },
  methods: {
    ...mapMutations(['setCustomerName']),
    updateName(event) {
      this.setCustomerName(event.target.value);
    }
  }
}

mapActions

// 原寫法
<template>
  <div>
    <p>{{ customerName }}</p>
    <input type="text" @input="updateName" :value="customerName" />
  </div>
</template>
<script>
import { mapState } from "vuex";
export default {
  name: "Example",
  computed: {
    ...mapState(['customerName'])
  },
  methods: {
    updateName(event) {
      this.$store.dispatch('setCustomerName', event.target.value);
    }
  }
}
</script>

// 使用 mapActions  輔助函數(shù)寫法

import { mapState, mapActions  } from 'vuex';
export default {
  name: "Example",
  computed: {
    ...mapState(['customerName'])
  },
  methods: {
    ...mapActions (['setCustomerName']),
    updateName(event) {
      this.setCustomerName(event.target.value);
    }
  }
}

createNamespacedHelpers

  • 如果要使用輔助函數(shù)引入vuex模塊化中的內(nèi)容怠缸,寫起來會(huì)比較繁瑣
  • 一般解決方法有如下2兩種
// 原寫法,引入模塊化后相對繁瑣
// Component.vue

computed: {
  ...mapState({
    a1: (state) => state.moduleA.a1,
    a2: (state) => state.moduleA.a2,
  })
},
methods: {
  ...mapActions({
    foo: 'moduleA/foo',
    boo: 'moduleA/boo',
  }),
}

//解決方法一:可以將模塊化的空間名稱作為第一個(gè)參數(shù)傳入輔助函數(shù)钳宪,這樣就會(huì)自動(dòng)添加 前綴名 于是上面的例子可以簡化為如下寫法:

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

// 解決方法二:通過使用 createNamespacedHelpers 創(chuàng)建基于某個(gè)命名空間輔助函數(shù)揭北。進(jìn)行自動(dòng)綁定,使其中能自動(dòng)查找綁定的模塊名

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

在vuex請求數(shù)據(jù)

  • 之前網(wǎng)上看到有這種寫法吏颖,本人不是很喜歡這種寫法搔体,建議還是按照正常接口方案寫

    //vuex 狀態(tài)文件
    
    import Vue from "vue";
    import Vuex from "vuex";
    import axios from "axios";
    Vue.use(Vuex);
    export default new Vuex.Store({
      state: {
        users: [],
        isLoading: false,
      },
      mutations: {
        setLoadingTrue(state) {
          state.isLoading = true;
        },
        setLoadingFalse(state) {
          state.isLoading = false;
        },
        setUsers(state, users) {
          state.users = users;
        },
        setCustomerName(state, name) {
          state.customerName = name;
        }
      },
      actions: {
        getUsers(context) {
          context.commit('setLoadingTrue');
          axios.get('/api/users')
            .then(response => {
              context.commit('setUsers', response.data);
              context.commit('setLoadingFalse');
            })
            .catch(error => {
              context.commit('setLoadingFalse');
              // handle error
            });
        }
      }
    });
    
    //組件文件
    <template>
      <div>
        <div id="spinner" v-if="isLoading">
          <img src="spinner.gif" />
        </div>
        <ul v-else>
          <li v-for="(user, index) in users" :key="index" >{{ user }}</li>
        </ul>
      </div>
    </template>
    <script>
    import { mapActions, mapState } from "vuex";
    export default {
      computed: {
        ...mapState([
          'isLoading',
          'users'
        ])
      },
      methods: {
        ...mapActions(['getUsers'])
      },
      created() {
        this.getUsers();
      }
    }
    </script>
    

vuex 模塊化文件結(jié)構(gòu)參考

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我們組裝模塊并導(dǎo)出 store 的地方
    ├── actions.js        # 根級別的 action
    ├── mutations.js      # 根級別的 mutation
    └── modules
        ├── cart.js       # 購物車模塊
        └── products.js   # 產(chǎn)品模塊
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市半醉,隨后出現(xiàn)的幾起案子嫉柴,更是在濱河造成了極大的恐慌,老刑警劉巖奉呛,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異夯尽,居然都是意外死亡瞧壮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門匙握,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咆槽,“玉大人,你說我怎么就攤上這事圈纺∏胤蓿” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵蛾娶,是天一觀的道長灯谣。 經(jīng)常有香客問我,道長蛔琅,這世上最難降的妖魔是什么胎许? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮罗售,結(jié)果婚禮上辜窑,老公的妹妹穿的比我還像新娘。我一直安慰自己寨躁,他們只是感情好穆碎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著职恳,像睡著了一般所禀。 火紅的嫁衣襯著肌膚如雪方面。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天北秽,我揣著相機(jī)與錄音葡幸,去河邊找鬼。 笑死贺氓,一個(gè)胖子當(dāng)著我的面吹牛蔚叨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辙培,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔑水,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扬蕊?” 一聲冷哼從身側(cè)響起搀别,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尾抑,沒想到半個(gè)月后歇父,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡再愈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年榜苫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翎冲。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垂睬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抗悍,到底是詐尸還是另有隱情驹饺,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布缴渊,位于F島的核電站赏壹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衔沼。R本人自食惡果不足惜卡儒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俐巴。 院中可真熱鬧骨望,春花似錦、人聲如沸欣舵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘圈。三九已至劣光,卻和暖如春袜蚕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绢涡。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工牲剃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雄可。 一個(gè)月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓凿傅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親数苫。 傳聞我的和親對象是個(gè)殘疾皇子聪舒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、企業(yè)項(xiàng)目開發(fā)流程 產(chǎn)品提需求 交互設(shè)計(jì)出原型設(shè)計(jì) 視覺設(shè)計(jì)出UI設(shè)計(jì)圖 前端開發(fā)出頁面模板 server端存取...
    Eric_V閱讀 1,754評論 0 3
  • ## Vuex Vuex是一個(gè)專為Vue.js應(yīng)用程序開發(fā)的**狀態(tài)管理模式**虐急。 調(diào)試工具:vue devtoo...
    flowerxuegao閱讀 220評論 0 0
  • 文檔:http://vuex.vuejs.org/zh-cn/modules.html這個(gè)modules是用來干什...
    杰森999閱讀 377評論 0 0
  • Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式 + 庫箱残。 它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)...
    月影WEB閱讀 394評論 0 1
  • vue中組件之間的通信 組件可以有以下幾種關(guān)系: [圖片上傳失敗...(image-8918e-166082302...
    流淚手心_521閱讀 104評論 0 0