Vuex学习

为什么要用Vuex?

  • 如果只是父对子数据交互,那么应该考虑使用 props 进行单向传递;
  • 如果涉及到子组件向父组件的数据传递,那么应该考虑使用 $emit$on
  • 涉及到非父子关系的组件,例如兄弟关系、祖孙关系,甚至更远的关系,他们之间如果有数据交互,那么应该使用Vuex来实现。

怎么使用?

准备Vuex实例

  • 安装vuex: npm install vuex --save
  • 准备vuex实例: export default new Vuex.Store({}),传入的是一个对象,其中的属性有state, getters, actions, mutations, modules。
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
  • main.js中注册vuex实例
new Vue({
  router,
  // 这可以把 store 的实例注入所有的子组件
  store,
  render: h => h(App)
}).$mount('#app')

state

在vuex实例中声明了一个state变量:persons,用来存储home.vue和about.vue这两个组件都需要用到的用户状态。

state: {
  persons: [{
    name: "张三",
    type: "VIP",
    rest: 100
  }, {
    name: "李四",
    type: "normal",
    rest: 0,
  }, {
    name: "王五",
    type: "VIP",
    rest: 0,
  }]
},

则在home.vue中用this.$store.state.persons,来得到这些用户状态。

getters

就像计算属性一样,可以对state进行进一步处理,当然这个处理最好是对于多个组件有意义。如果是针对于某个组件有意义,则完全可以写到该组件的computed中。
比如home.vue和about.vue都需要使用VIP客户信息,则可以在getters中筛选。

getters: {
  // vipShow(state, getters), 通过state参数获得状态,通过getters参数获得其他处理状态。
  vipShow(state) {
    return state.persons.filter(p => p.type == "VIP")
  }
},

在使用到该状态的组件中,用this.$store.getters.vipShow来得到处理后的用户状态。

如果需要筛选出VIP剩余天数大于t天的用户,即this.$store.getters.vipShow(t)的使用场景:有参数传递的情况下,getters该如何处理?
这也是最妙的一个地方,这个参数并没有传递到getters中的vipShow参数中,而是通过返回一个函数 来实现的。

getters: {
  vipShow2(state) {
    return t => state.persons.filter(p => p.type == "VIP" && p.rest > t)
  }
},

mutations

mutations类似于methods,用来定义一些对状态的操作。

mutations: {
  // mutations中的函数同样接收一个state,也接收一个参数对象payload。
  // 这个方法用来对每个用户VIP续费num天。
  increment(state, payload) {
    return state.persons.map(p => p.rest += payload.num)
  }
},

但是与methods不同的是,mutations中的函数 只能通过commit触发,不能手动调用

(组件)
<button @click="btnClickHandler">续费一年</button>
methods: {
  btnClickHandler() {
    this.$store.commit("increment", { num: 365 })
  }
}

mutations的响应式

  • 如果state中有个状态(如persons)是数组,需要修改元素值,直接修改,则无法响应到View中。
    // Model层改了,但View没改。
    // state.persons[0] = {
    //   name: "赵七",
    //   type: "normal",
    //   rest: 0
    // }
    
    Vue.set(state.persons, 0, {
      name: "赵七",
      type: "normal",
      rest: 0
    })
    
  • 如果state中有个状态(如info)是对象,需要增加属性,使用Vue.set(obj, key, value)
    info: {
      name: 'kobe',
      age: 18
    }
    -----------------------------
    // Model层改了,但View没改。
    state.info.height = "1.88";
    // 方式一:
    Vue.set(state.info, "height", "1.88")
    // 方式二:
    state.info = { ...state.info, "height": "1.88" }
    

actions

实际上,mutations也可以包含异步函数,程序能够正常运行。不过因为在devtools中无法追踪,因此建议把异步函数放到actions中。
看下面一个例子:
假如倒计时3s后,将所有用户提升为VIP。

actions: {
  // context是一个对象, 与store实例相同方法和属性。context.state, context.getters, context.commit(),...
  setAllVIP(context) {
    setTimeout(() => {
      context.state.persons.map(p => p.type = "VIP")
    }, 3000);
  }
},

然后在组件中this.$store.dispatch("setAllVIP"),就能提升所有用户为VIP了。虽然这样写确实可以达到效果,但是并不是官网推荐(如下图所示)的做法,而且这么做同样不能使得devtools能够追踪到变化。
在这里插入图片描述
因此,按照上图的做法,应该这么写:

mutations:{
  setVIP(state) {
    state.persons.map(p => p.type = "VIP")
  }
}, 
actions: {
  setAllVIP(context) {
    setTimeout(() => {
      context.commit("setVIP")
    }, 3000);
  }
},

组件中写法不变:this.$store.dispatch("setAllVIP"),值得一提的是,dispatch函数返回的是setAllVIP函数的返回值。
因此如果希望异步操作完成后,能够打印一下提示信息,可以将setAllVIP函数中的异步操作包裹在一个Promise中,返回。

actions: {
  setAllVIP(context) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        context.commit("setVIP")
        resolve("升级成功了");
      }, 3000);
    })
  }
},

(组件)
this.$store.dispatch("setAllVIP").then(res => {
  console.log(res);   // 升级成功了
})

module

  • module中的模块会被当做一个状态放到全局state中。因此使用时$store.state.moduleA
  • 模块内部的 action、mutation 和 getter 是注册在全局命名空间的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值