Vue.js

Vuex简单使用

2021-12-23  本文已影响0人  叽里咕呱

一、初始化Vuex

Vuex是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
如果一份数据需要在多个组件中使用,组件间传值又比较复杂,就可以使用vuex托管数据。

1、安装

npm install vuex --save

2、导入

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

3、创建状态管理对象 store

state选项:定义状态(状态就是数据)。
mutations选项:定义修改状态的方法(注意:这里面只能定义同步方法)。

export default new Vuex.Store({
  // 定义全局状态(状态就是数据)
  state:{
    car:{
        name:'奔驰',
        price:'40W'
    }
  },
  // 定义修改状态的方法
  mutations:{
    //该方法,修改汽车信息
    updateCar(state,val){
        state.car = val
    }
  }
})

4、注册给Vue

// 导入当前项目中的全局状态管理对象
import store from './store'
new Vue({
  // 在vue实例中使用全局状态管理对象
  store,
  render: h => h(App)
}).$mount('#app')

5、简单使用

$store:返回的是当前项目中的全局状态对象。
commit()方法:用于执行指定的mutations里面的方法。

(1)获取数据

在组件中,直接通过$store.state就可以获取到全局状态对象管理的状态数据,直接渲染到页面。

<div>车辆名称:{{ $store.state.car.name }}</div>
<div>车辆价格:{{ $store.state.car.price }}</div>

(2)修改数据

<div>车辆名称:{{ $store.state.car.name }}</div>
<div>车辆价格:{{ $store.state.car.price }}</div>
<button @click="updateCar">修改汽车信息</button>
methods: {
  updateCar() {
    this.$store.commit("updateCar", { name: "奔驰", price: "60W" });
  }
}

二、核心概念

1、state

state选项:定义状态(状态就是数据)。

  state: {
    name:'张三'
  }

通过$store.state.数据名使用。

<div>姓名:{{ $store.state.name }}</div>

2、getters

getters选项:定义计算属性。方法的参数是状态对象。

  getters:{
    // 方法的第一个参数是全局状态
    nameInfo(state){
      return `我的名字叫${state.name}`
    }
  }

通过$store.getters.属性名使用计算属性。

<div>{{ $store.getters.nameInfo }}</div>

3、mutations

mutations选项:定义修改状态的方法(注意:这里的方法一般都是同步方法)。方法的第一个参数是状态对象,第二个参数是新值。

  mutations:{
    // 修改姓名
    updateName(state,val){
      state.name = val
    }
  },

通过commit()方法,调用mutations里面的方法。

this.$store.commit("updateName", '李四');

4、actions

actions选项:定义操作状态的方法(这里的方法可以定义异步方法)。
注意:actions里的方法最好不要直接操作state状态,而是通过调用mutations里面的方法去修改状态。所以,actions直接操作的是mutations。

  state: {
    carAddress:'意大利'
  },
  mutations:{
    //修改汽车的产地
    updateCarAddress(state,val){
      state.carAddress = val
    }
  },
  actions:{
    //修改汽车的产地
    //方法的第一个参数是全局状态管理对象,第二个参数是具体的值
    updateCarAddress(store,val){
      axios.get(val).then(({data})=>{
        // 方式一:这里可以直接修改状态
        // store.state.carAddress = data.address
        // 方式二:通过调用mutations里面的方法修改状态
        store.commit('updateCarAddress',data.address)
      })
    }
  }

通过dispatch()方法,调用actions里面定义的方法。

this.$store.dispatch('updateCarAddress','data/address.json')

5、modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

(1)定义模块

namespaced属性:默认情况下,action、mutation 和 getter 是注册在全局命名空间的。通过设置namespaced属性为true,将action、mutation 和 getter全部注册到私有命名空间中。

export default {
    namespaced:true,
    // 状态
    state:{
        planeName:'空客404',
        planePrice:'10Y',
        planeAddress:'中国'
    },
    // 计算属性
    getters:{
        planeInfo(state){
            return `飞机名称:${state.planeName},飞机价格:${state.planePrice},飞机产地:${state.planeAddress}`
        }
    },
    // 同步方法
    mutations:{
        updatePlaneName(state,val){
            state.planeName = val
        },
        updatePlanePrice(state,val){
            state.planePrice = val
        }
    },
    // 异步方法
    actions:{
        updatePlanePrice(store,val){
            setTimeout(() => {
                store.commit('updatePlanePrice',val)
            }, 500);
        }
    }
}

(2)在全局状态管理对象中导入模块

// 导入飞机模块
import plane from './modules/plane.js'

// 创建一个全局状态管理对象并导出
export default new Vuex.Store({
  // 模块
  modules:{
    // 飞机模块
    plane
  }
})

(3)使用模块

① 获取模块中的state状态

要从私有模块中获取数据,方式是:$store.state.模块名称.模块的数据

<div>飞机名称:{{ planeName }}</div>
planeName() {
  return this.$store.state.plane.planeName;
}

② 获取模块中的getters计算属性

要从私有模块中获取计算属性,方式是:$store.getters['模块名/计算属性']

<div>{{planeInfo}}</div>
planeInfo(){
    return this.$store.getters['plane/planeInfo']
}

③ 调用模块中的mutations定义的方法

调用私有模块里面的mutations定义的方法,方式是:$store.commit('模块名/方法名',新值)

<button @click="updatePlaneName">修改飞机名称</button>
updatePlaneName(){
  this.$store.commit('plane/updatePlaneName','波音747')
}

④ 调用模块中的actions定义的方法

调用私有模块里面的actions定义的方法,方式是:$store.dispatch('模块名/方法名',新值)

<button @click="updatePlanePrice">修改飞机价格</button>
updatePlanePrice(){
  this.$store.dispatch('plane/updatePlanePrice','20Y')
}

三、Vuex使用

1、计算属性中转

直接在模板中使用全局状态管理数据,表达式会写的很长。所以可以使用计算属性。

  // getters选项定义计算属性
  getters:{
    carInfo(state){
      return `汽车名称:${state.carName},汽车价格:${state.carPrice},汽车产地:${state.carAddress}`
    }
  }
 <!-- 直接在模板中使用全局状态里面的计算属性 -->
 <div>{{$store.getters.carInfo}}</div>
 <div>{{carInfo}}</div>
  //计算属性
  computed:{
    // 汽车信息
    carInfo(){
      // 返回全局状态管理里面的计算属性
      return this.$store.getters.carInfo
    }
  }

2、映射函数

通过映射函数mapState、mapGetters、mapActions、mapMutations,可以将vuex.store中的属性映射到vue实例身上,这样在vue实例中就能访问vuex.store中的属性了,便于操作vuex.store。

(1)导入映射函数

// 从vuex中,导入映射函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";

(2)使用映射函数生成计算属性

如果vuex里面state的数据名称 跟 页面中的计算属性名称相同,就可以使用mapState映射函数,自动生成页面中的计算属性。
如果vuex里面getters的数据名称 跟 页面中的计算属性名称相同,就可以使用mapGetters映射函数,自动生成页面中的计算属性。
注意:如果要映射模块里面的state/getters,函数的第一个参数设置为模块的名称。

<div>汽车名称:{{ carName }}</div>
<div>汽车价格:{{ carPrice }}</div>
<div>汽车产地:{{ carAddress }}</div>
<div>{{ carInfo }}</div>
<hr/>
<div>飞机名称:{{ planeName }}</div>
<div>飞机价格:{{ planePrice }}</div>
<div>飞机产地:{{ planeAddress }}</div>
<div>{{ planeInfo }}</div>
computed: {
  // mapState映射state
  ...mapState(["carName", "carPrice", "carAddress"]),
  // mapGetters映射getters
  ...mapGetters(["carInfo"]),

  // 映射私有模块里面的数据
  ...mapState('plane',['planeName','planePrice','planeAddress']),
  ...mapGetters('plane',['planeInfo'])
}

(3)使用映射函数生成方法

如果定义的方法名跟全局管理对象中mutations里面的方法名相同,并且定义的方法会带有一个参数,通过参数传递数据。满足该规则,就可以使用mapMutations映射函数生成方法。
如果定义的方法名跟全局管理对象中actions里面的方法名相同,并且定义的方法会带有一个参数,通过参数传递数据。满足该规则,就可以使用mapActions映射函数生成方法。
注意:如果要映射私有模块中mutations/actions里面的方法,函数的第一个参数设置为模块的名称。

<button @click="updateCarName('宾利')">修改汽车名称</button>
<button @click="updateCarPrice('300W')">修改汽车价格</button>
<button @click="updateCarAddress('data/address.json')">修改汽车产地</button>
<hr/>
<button @click="updatePlaneName('波音747')">修改飞机名称</button>
<button @click="updatePlanePrice('20Y')">修改飞机价格</button>
methods: {
  // 映射全局管理对象中mutations里面的方法
  ...mapMutations(["updateCarName", "updateCarPrice"]),
  // 映射全局管理对象中actions里面的方法
  ...mapActions(["updateCarAddress"]),

  // 映射私有模块里面的方法
  ...mapMutations('plane',['updatePlaneName']),
  ...mapActions('plane',['updatePlanePrice'])
}

3、购物车模块

import axios from "axios";
export default {
  namespaced: true,
  state: {
    //商品数组
    goodses: [],
  },
  getters: {
    //总价
    totalPrice(state) {
      return state.goodses.filter((r) => r.ck).map((r) => r.price * r.count).reduce((a, b) => a + b, 0);
    },
    // 是否全选
    isCkAll(state) {
      return state.goodses.length>0 & state.goodses.every((r) => r.ck);
    },
  },
  mutations: {
    // 加载数据的同步方法
    loadGoodses(state, val) {
      state.goodses = val;
    },
    // 设置所有商品的状态
    ckAll(state, val) {
      state.goodses.forEach((r) => {
        r.ck = val;
      });
    },
    // 根据id删除商品
    delGoodsById(state, id) {
      let index = state.goodses.findIndex((r) => r.id == id);
      state.goodses.splice(index, 1);
    },
  },
  actions: {
    // 加载数据的异步方法
    loadGoodses(store, val) {
      axios.get(val).then(({ data }) => {
        store.commit("loadGoodses", data);
      });
    },
    // 设置所有商品的状态
    ckAll(store, val) {
      store.commit("ckAll", val);
    },
    // 根据id删除商品
    delGoodsById(store, id) {
        store.commit("delGoodsById", id);
    },
  },
};
<template>
  <div class="shopcart">
    <h4>购物车</h4>
    <table>
      <thead>
        <tr>
          <th>
            <input type="checkbox" v-model="isCkAll" />
          </th>
          <th>名称</th>
          <th>图片</th>
          <th>单价</th>
          <th>数量</th>
          <th>小计</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in goodses" :key="item.id">
          <td>
            <input type="checkbox" v-model="item.ck" />
          </td>
          <td>{{ item.name }}</td>
          <td>
            <img :src="item.img" />
          </td>
          <td>{{ item.price }}</td>
          <td>
            <button @click="item.count--" :disabled="item.count === 1">
              -
            </button>
            <input type="text" v-model="item.count" />
            <button @click="item.count++" :disabled="item.count === 10">
              +
            </button>
          </td>
          <td>{{ item.price * item.count }}</td>
          <td>
            <button @click="delGoodsById(item.id)">删除</button>
          </td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="7">
            <span>总价:</span>
            <span>{{ totalPrice }}</span>
          </td>
        </tr>
      </tfoot>
    </table>
  </div>
</template>
<script>
import { mapState, mapGetters, mapActions } from "vuex";
export default {
  name: "Shopcart",
  computed: {
    ...mapState("shopcart", ["goodses"]),
    ...mapGetters("shopcart", ["totalPrice"]),
    // 定义可以修改的计算属性
    isCkAll: {
      set(val) {
        this.ckAll(val);
      },
      get() {
        return this.$store.getters["shopcart/isCkAll"];
      },
    },
  },
  methods: {
    // 映射actions对应的方法
    ...mapActions("shopcart", ["ckAll", "delGoodsById"]),
  },
  // 页面挂载完成
  mounted() {
    this.$store.dispatch("shopcart/loadGoodses", "data/shopcart.json");
  },
};
</script>
购物车效果图
上一篇下一篇

猜你喜欢

热点阅读