Vue状态管理插件-Vuex入门


Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex是在组件外部管理状态,是用来管理组件之间通信的一个插件。这里的状态就是指数据。

这一次我们用Vuex来模拟一个类似购物车的例子,这里并没有实现真正的购物车,目的是用最简单的代码演示Vuex的知识点。
在这里插入图片描述

安装vuex

和安装vue.js一样,直接用

<script src="./vue.js"></script>
<script src="./vuex.js"></script>

引入后,相当于注册了全局属性Vuex
在这里插入图片描述

创建store

Vue中每个应用将仅仅包含一个 store实例,store可以理解为仓库。
下面是本例中代码的结构,首先创建一个store实例,参数是个option对象,其中包含属性state、getters、mutations、actions。

<div id="app">
</div>
<script src="./vue.js"></script>
<script src="./vuex.js"></script>
<script>
const myStore = new Vuex.Store({
  state: {},
  getters:{},
  mutations:{},
  actions:{},
});
const {mapState,mapGetters,mapActions,mapMutations} =Vuex;
new Vue({
});
</script>

Store方法的参数:

state

在state中存放状态(数据),它的值是一个对象,是供我们全局使用的状态。

本例中,定义一个数组代表购物车列表

  state: {
  	shoppingCart: [{id: 101, name: '苹果', count: 1, price: 2}]
  },

getters

从 state 中可以派生出一些状态,如本例中,购物车里的数据可以派生出总金额,这个数据依赖于state,派生状态保存在getters中, getters类似Vue的computed属性,是store的计算属性,与computed属性一样,getter的返回值会根据它的依赖被缓存起来,只有当它的依赖值发生了改变才会被重新计算。
参数:Getter 接受 state 作为其第一个参数。

getters : {
  //购物车里商品总金额
  totalMoney:(state)=>state.shoppingCart.reduce((prev, current)=> prev + current.count * current.price, 0)
 },

mutations

在mutations选项中定义mutation方法,用于修改state,规定更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
参数:mutation方法中接受两个参数,第一个是state,第二个是用户传入的参数,这里称为payload,格外的参数,可选。
调用方式:mutation方法通过 store.commit('mutation方法名',值)方法触发,实现同步操作store中的数据。

本例中,只有两个mutation方法,add方法用于给购物车增加一条记录,remove方法用于删除一条记录。

 mutations: {
    //add方法判断购物车中是否有添加的商品,如果有增加数量,如果没有添加一条记录
    add: (state, payload) => {
      let isHas = state.shoppingCart.some((item) => {
        if (item.id == payload.id) {
          item.count += payload.count;
          return true;
        }
      })
      if (!isHas) state.shoppingCart.push(payload);
    },
    //购物车中移除一条记录,这里用es6解构赋值写法
    remove: ({shoppingCart}, id) => {
      let i = shoppingCart.findIndex((item) => item.id === id);
      shoppingCart.splice(i, 1);
   }
 },

actions

从store的外部看action方法和mutation方法都用于修改state,但在store内部,实际上action不能直接操作state,在其内部需要去触发mutation方法来实现修改State。

  • action方法用于定义异步操作逻辑,以满足某些业务场景要求。
  • action方法通过 store.dispatch(‘action方法名’,值) 方法触发。
  • action方法接受 context对象作为第一个参数,payload 作为第二个参数(可选)

context 对象与store 实例具有相同方法和属性。
在这里插入图片描述
本例中,假设业务逻辑是添加购物车时需要到后端验证库存余额,这里用setTimeout模拟一个异步请求,异步成功后触发mutation方法add,在mutation方法中修改state,这是store的内部逻辑,在store之外看调用action方法即修改了state。

actions: {
	add:(context, payload)=>{
	  setTimeout(()=>{
	    let code = 0;
	    if (code === 0) {
	      context.commit('add', payload);  //触发mutation方法
	    }
	  },2000 * Math.random())
	}
},

现在完成了这个例子中store的创建,在控制台中打印一下这个对象查看一下。
在这里插入图片描述

Vue部分

模板

本例中使用了2个组件,根组件和购物车组件

<div id="app">
  <div class="productList">
    <h2>商品目录</h2>
    <table border="1">
      <tr>
        <th>编号</th>
        <th>商品名称</th>
        <th>单价</th>
        <th>数量</th>
        <th>操作</th>
      </tr>
      <tr v-for="(item,i) in productList" :key="item.id">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.price }}</td>
        <td>{{item.count}}</td>
        <td>
          <button v-on:click="add({id:item.id,name:item.name,price:item.price,count:1})">添加到购物车</button>
        </td>
      </tr>
    </table>
  </div>
  <shopping-cart inline-template>
    <div class="shoppingCart" v-if="shoppingCart.length>0">
      <h2>购物车</h2>
      <table border="1">
        <tr>
          <th>编号</th>
          <th>商品名称</th>
          <th>单价</th>
          <th>数量</th>
          <th>操作</th>
        </tr>
        <tr v-for="item in shoppingCart" :key="item.id" >
          <td>{{item.id}}</td>
          <td>{{item.name}}</td>
          <td>{{item.price }}</td>
          <td>{{item.count}}</td>
          <td>
            <button v-on:click="remove(item.id)">删除</button>
          </td>
        </tr>
      </table>
      <p>总金额:{{totalMoney}}元</p>
    </div>
  </shopping-cart>
</div>

Vue实例

在Vue中获取和操作Store之前,首先要引入Store,在vue实例的store选项中引入Store实例,这样做使得整个实例(包含子组件)都可以通过this.$store获取到定义的Store实例。

new Vue({
    el: "#app",
    store:myStore,  // 把创建的myStore对象提供给vue实例的store选项
    data: {
      productList: [
        {id: 101, name: '苹果', count: 5, price: 2},
        {id: 102, name: '香蕉', count: 10, price: 3},
        {id: 103, name: '菠萝', count: 15, price: 4}
      ]
    },
    components: {
      'shopping-cart': {
        computed: {
          shoppingCart(){
            return this.$store.state.shoppingCart;
          },
          totalMoney() {
            return this.$store.getters.totalMoney;
          }
        },
        methods: {
          remove(id) {
            this.$store.commit('remove', id);
          }
        }
      }
    },
    methods: {
      add(product) {
        this.$store.dispatch('add', product);
      }
    },
});

Vuex中的辅助函数

在前面的代码中有这样一句:

const {mapState,mapGetters,mapActions,mapMutations} =Vuex;

定义了这些常量,但并没有使用,实际上他们是Vuex中提供了4个辅助函数,用于将vuex.store中的属性映射到vue实例上,为在vue实例中获取和操作vuex.store提供便利。

下面将使用辅助函数替换掉原来的代码

在这里插入图片描述

mapState辅助函数

mapState是state的辅助函数,用于把state属性映射到computed上。

  • mapState函数接受一个对象或数组作为参数;
  • 返回一个数组对象,数组成员形式为键名:函数

用一个例子理解一下mapState函数,其他几个辅助函数与之相似

  const {mapState,mapGetters,mapActions,mapMutations} =Vuex;
  let obj={a:1,b:2};
  console.log(obj,'->',Vuex.mapState(obj));
  console.log(Vuex.mapState(obj),'->',{...Vuex.mapState(obj), c:3});
  console.log('------------------------------------');
  let arr=['a','b'];
  console.log(arr,'->',Vuex.mapState(arr));
  console.log(Vuex.mapState(arr),'->',{...Vuex.mapState(arr), c:3});

用对象展开运算符(…)可以将对象混入到外部对象中。
在这里插入图片描述
懂了mapState辅助函数的作用,我们来看一下其他几个辅助函数

const {mapState,mapGetters,mapActions,mapMutations} =Vuex;
console.log(myStore.state,'->',mapState(myStore.state));
console.log(myStore.getters,'->',mapGetters(myStore.getters));
console.log(myStore._actions,'->',mapActions(myStore._actions));
console.log(myStore._mutations,'->',mapMutations(myStore._mutations));

在这里插入图片描述
替换shopping-cart组件中的computed中的代码。

'shopping-cart': {
	computed: {
	  ...mapState(['shoppingCart']), //当映射的计算属性的名称与 state的子节点名称相同时,可以给 mapState 传一个字符串数组。
	  totalMoney() {
	    return this.$store.getters.totalMoney;
	  }
	},
}

也可以这样写

...mapState({
  shoppingCart:'shoppingCart'  // 传字符串参数 'shoppingCart' 等同于 `state => state.shoppingCart`
}),

PS:mapState可以接受数组或者对象作为参数,在大型项目中给mapState传递一个对象会更合适,shoppingCart实际上是一个别名,这样可以解决命名冲突的问题,也可以代替state里过长的属性名。

mapGetters辅助函数

把getters属性映射到computed上,mapGetters函数和mapState相似

mapActions辅助函数

把actions里面的方法映射到methods中

mapMutations辅助函数

把mutations里面的方法映射到methods中

使用前面的4个辅助函数对代码进行改写

new Vue({
    el: "#app",
    store,  
    data: {
      productList: [
        {id: 101, name: '苹果', count: 5, price: 2},
        {id: 102, name: '香蕉', count: 10, price: 3},
        {id: 103, name: '菠萝', count: 15, price: 4}
      ]
    },
    components: {
      'shopping-cart': {
        computed: {
          ...mapState(['shoppingCart']), // 把this.shoppingCart 映射为 this.$store.state.shoppingCart
          ...mapGetters(['totalMoney']) //把this.totalMoney映射为this.$store.getters.totalMoney
        },
        methods: {
          ...mapMutations(['remove']) //把this.remove()映射为 this.$store.commit('remove')
        }
      }
    },
    methods: {
      ...mapActions(['add']) //将this.add()映射为this.$store.dispatch('add')
    },
  });

Vuex存储和本地存储

  • Vuex存储的状态(数据)以对象的形式保存在内存中,当刷新页面(清除内存)时vuex存储的值会丢失;
  • localstorage(本地存储)以文件的方式存储在本地,永久保存;
  • sessionstorage( 会话存储 )以文件的方式存储在本地,浏览器关闭即被清除。
    localStorage和sessionStorage只能存储字符串类型,可以使用ECMAScript提供的JSON对象的stringify和parse来处理字符串。

Vuex用于组件之间的传值,localstorage,sessionstorage则主要用于不同页面之间的传值,在单页Vue应用中,似乎它们之间可以互相代替。实际在真实业务中,它们是结合使用的,浏览器无法记住Vuex存储的数据(数据保存在内存)一般会采用localStorage、sessionStorage或者cookie等来保存需要浏览器记住的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值