vuex 数据管理

vue 数据管理

在实际的开发中,经常会遇到多个组件共享一个数据的场景

在这里插入图片描述

面对这种场景,会产生至少以下两个问题:

  1. 多个组件如何共享同一份数据?
  2. 如果某个组件修改了数据,如何让其他组件知道?

面对这种问题,一个可行的解决办法,就是让数据提升

所谓数据提升,就是把数据提升到更加顶层的组件,让顶层的组件通过属性下发数据,而当组件想改变数据的时候,又通过事件一层层向上传递

在这里插入图片描述

使用这种方式,虽然可以解决问题,但是带来了更多的问题:

  1. 书写特别繁琐
  2. 依赖极其混乱:某些组件本来并不需要一些数据,但是由于它的子组件需要,自己也必须要接收
  3. 无谓的重新渲染:如果数据变化了,并不依赖这些数据的组件也会被迫重新渲染

为了解决这些问题,vuex出现了

vuex专门用于解决共享数据问题,它的思路和上述一致,也是将数据提升到顶层,不过它使用了一些特别的技巧,不仅让组件的依赖更加清晰,当数据变动时,仅冲渲染依赖该数据的组件

但是要注意,并非所有数据都需要让vuex管理,通常vuex只管理那些需要被组件共享的数据

在实际的开发中,一些逻辑特别复杂的数据,尽管不共享,也可能提取到vuex中进行管理

安装

在页面中引入vuex

该库提供了一个构造函数Vuex.Store,通过该构造函数,即可创建一个数据仓库

var store = new Vuex.Store({
  // 仓库数据配置
})

将得到的对象,配置到vue中,即可在vue中注入vuex的功能

new Vue({
  // 其他配置
  store
})

现在,你的vue应用拥有了使用数据仓库的能力

初始化状态

vuex将仓库中的数据称之为state 状态

vuex的配置中使用属性state即可配置仓库中的状态初始值

var store = new Vuex.Store({
  state: { // 状态初始值,这些数据可以被所有组件共享
    onlineNumber: 0, // 在线人数
    movies: { // 电影数据
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
    }
  }
})

在组件中使用该数据非常简单

vuexvue实例中注入了一个属性$store,通过该属性即可得到仓库中的数据

this.$store.state // 仓库中的状态
<h1>
在线人数:{{$store.state.onlineNumber}}
</h1>

虽然这种写法看上去很方便,但是书写繁琐,并且不利于从阅读上明确依赖关系

更多的时候,我们会将组件对数据的依赖,明确的声明在组件的computed配置中

var Comp = {
  computed:{
    onlineNumber(){
      return this.$store.state.onlineNumber;
    }
  },
  template: `
    <h1>
    在线人数:{{onlineNumber}}
    </h1>
  `
}

为了更加方便的让我们书写computedvuex提供了一个函数vuex.mapState

var Comp = {
  computed: Vuex.mapState(["onlineNumber"]), // 等同于上面的代码
  template: `
    <h1>
    在线人数:{{onlineNumber}}
    </h1>
  `
}

vuex.mapState有非常非常多的用法,目的只有一个:更加方便的书写computed

状态模块化

通常情况下,一个vue实例,只有一个数据仓库

如果仓库中的所有状态都放在一起,既不利于管理,也容易产生名称的冲突

实际开发中,仓库中的状态往往是分为多个模块的

var movies = { // 电影模块
  state: { // 电影模块的初始状态
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  }
}

var online = { // 在线统计模块
  state: {
    number: 0
  }
}

// 合并状态模块

var store = new Vuex.Store({
  modules:{
    movies,
    online
  }
})

从此,store中的状态如下:

{
  online:{
    number:0
  },
  movies:{
    data: [],
    total: 0,
    page: 1,
    limit: 3,
    isLoading: false
  }
}

在使用时,可以通过下面的方式映射到computed

var Comp = {
  computed: Vuex.mapState("online", ["number"]), // 第一个参数是模块名称(命名空间名)
  template: `
    <h1>
    在线人数:{{number}}
    </h1>
  `
}

数据变化

数据不可能永远不变,但数据也不会无缘无故的变化

每一次数据变化都有原因,在某种原因的驱使下,数据从一种状态变化到另一种状态

vuex把数据的变化过程,称之为mutation

mutation在代码中表现为一个函数,配置在mutations

var movies = { // 电影模块
  state: { // 电影模块的初始状态
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  },
  mutations:{
    /**
     * setPage: mutation的名称
     * oldState: 原来的状态
     * payload: 载荷。为了变化新状态,需要的额外信息
     */ 
    setPage(oldState, payload){
      oldState.page = payload.page;
    },
    setResp(oldState, payload){
      oldState.data = payload.data;
      oldState.total = payload.total;
    }
  }
}

看上去,mutation似乎非常简单,仅仅是完成一个赋值即可

但它的意义是非凡的

它至少向外表达了以下信息:

  • 我的数据可以发生哪些变化
  • 除了这些变化之外,不可能再发生任何其他变化
  • 这些变化是原子性的,不可分割的。比如setResp,必须同时变化datatotal,不可能分割

mutation的存在,让状态变化变得统一、可控

通过mutation触发数据的变化,称之为commit 提交

提交mutation,是数据发生变化的唯一原因

在这里插入图片描述

在代码层面,提交mutation通过下面的api完成

// 触发setPage运行,payload为2
this.$store.commit("setPage", {page:2});

this.$store.commit({
  type: "setPage",
  page: 2
})

当仓库中的状态变化时,所有依赖该状态的组件都会自动重新渲染

追踪数据变化

mutation让追踪数据变化成为了可能,这非常有利于调试

chrome扩展程序vue-devtools

同时,这也要求我们在编写mutation时,要注意:

mutation 中不要出现异步代码,否则会让状态的变化难以追踪

启用命名空间

由于数据模块后,非常容易出现mutation重名

启用命名空间即可解决该问题

var movies = { // 电影模块
  namespaced: true, // 启用命名空间
  state: { 
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  },
  mutations:{
    setPage(oldState, payload){
      oldState.page = payload.page;
    },
    setResp(oldState, payload){
      oldState.data = payload.data;
      oldState.total = payload.total;
    }
  }
}

命名空间启用后,commit时,mutation的名称前需要加上命名空间

this.$store.commit("movies/setPage", { page: 1 })

异步处理

由于mutation不允许使用异步代码,所以异步代码需要单独处理

vuex把异步处理称之为action

action中不允许直接更改状态,但允许提交mutation

var online = {
  namespaced: true,
  state:{
    number: 0
  },
  mutations:{
    add(state){
      state.number++;
    }
  },
  actions:{ //处理异步
    asyncAdd(context){
      // 可以把context当作是store对象
      setTimeout(function(){
        context.commit("add")
      }, 1000)
    }
  }
}

action中,你可以通过参数context参数,获取store实例

但值得注意的是,如果开启了namespaced,则contextstore有以下不同:

  • context.state是当前模块的状态,而不是整个仓库的状态
  • context.rootState才是整个仓库的状态
  • context.commit可省略当前命名空间

如果要触发一个action,需要使用下面的API

this.$store.dispatch("online/asyncAdd");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值