今天看啥  ›  专栏  ›  lio_zero

Vuex

lio_zero  · 简书  ·  · 2021-04-10 21:59

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 ,是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据的共享。

安装

$ npm install vuex
# or
$ yarn add vuex

在 vue 实例中注册 store

new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app")

通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。

你可以在👉 Store 测试效果。

State

State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 state 中进行储存

state: {
  counter: 1,
  msg: "hello world!"
}

获取方式一

this.$store.state.msg // hello world!

获取方式二

使用 mapState 函数,将全局数据映射为当前组件的 computed 计算属性使用

import { mapState } from 'vuex'

...mapState(['counter', 'msg'])

Mutations

Mutation 用于变更 Store 中的状态,但它总是同步的。

  • Mutation 是改变存储中状态数据的唯一方法。

  • 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化

mutations: {
    // 定义事件处理函数
  increment(state) {
    // 变更状态
    state.counter++
  },
  // 可以在触发 mutations 时传递参数:
  incrementN(state, step) {
    state.counter += step
  },
  decrementN(state, step) {
    state.counter += step
  }
}

调用方式一

// 调用 commit 函数
this.$store.commit('increment')
// 触发 mutations 时携带参数
this.$store.commit('incrementN', 3) 

调用方式二

使用 mapMutations 函数,映射为当前组件的 methods 函数

import { mapMutations } from 'vuex'

...mapMutations(['increment', 'incrementN'])

Actions

Action 将允许我们异步更新状态,但将使用现有的 mutation。如果你需要按照特定的顺序同时执行几个不同的 mutation,这会非常有帮助。

actions: {
  asyncDecrementN({ commit }, asyncNum) {
    setTimeout(() => {
      commit("decrementN", asyncNum.by)
    }, asyncNum.duration)
  }
}

获取异步方法

// 触发 actions 异步任务时携带参数
asyncDecrementN() {
  this.$store.dispatch("asyncDecrementN", {
    by: -3,
    duration: 1000,
  })
},

Getters

Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性

  • Getter 将使值能够在模板中静态显示。换句话说,getter 可以读取值,但不能改变状态。

  • Store 中数据发生变化,Getter 的数据也会跟着变化

// 展示数据,而不是更改状态
getters: {
  tripleCounter: (state) => state.counter * 3
}

获取方式一

$store.getters.tripleCounter

获取方式二

使用 mapGetters 函数,将全局数据映射为当前组件的计算属性使用

import { mapGetters } from 'vuex'

computed: {
  ...mapGetters(['tripleCounter'])
}

Modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成 模块(module) 。每个模块拥有自己的 state、mutation、action、getter。

// module/moduleA.js
const moduleA = {
 state: () => ({
 name: "李四"
 }),
 mutations: {},
 actions: {},
 getters: {}
};

export default moduleA;

// module/moduleB.js
const moduleB = {
 state: () => ({
 name: "老六"
 }),
 mutations: {},
 actions: {}
};

export default moduleB;

// store/index.js
import moduleA from "./module/moduleA"
import moduleB from "./module/moduleB"
const store = new Vuex.Store({
 modules: {
 moduleA,
 moduleB
 }
})

store.state.moduleA // -> moduleA 的状态
store.state.moduleB // -> moduleB 的状态

详细信息请看 modules

其他

热重载

Vuex 支持在开发过程中 热重载 mutation、module、action 和 getter。

项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  • 应用层级的状态应该集中到单个 store 对象中。
  • 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  • 异步逻辑都应该封装到 action 里面。

如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

插件

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:

const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, payload }
  })
}

然后像这样使用:

const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})

表单处理

在 Vuex 严格模式下,状态变更需要在 mutation 函数下执行。在你不了解这个的情况下,使用 v-model 时,可能会发生一些错误。

<input v-model="obj.message">

假设这里的 obj 是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时, v-model 会试图直接修改 obj.message 。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。

方式一:给 <input> 中绑定 value,然后侦听 input 或者 change 事件,在事件回调中调用一个方法

<input :value="message" @input="updateMessage">

<script>
export default {
  computed: {
    ...mapState({
      message: state => state.obj.message
    })
  },
  methods: {
    updateMessage (e) {
      this.$store.commit('updateMessage', e.target.value)
    }
  }
}
</script>

mutation 函数

mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}

方式二:使用 setter 的双向绑定计算属性

computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = new Vuex.Store({
  // ...
  strict: true
})

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

不要在发布环境下启用严格模式 !严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。

类似于插件,我们可以让构建工具来处理这种情况:

const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

常见的问题

什么时候使用 Vuex?

Vuex 通过全局注入 store 对象,来实现组件间的状态共享。

开发大型单页应用时(多级组件嵌套),需要实现一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用 vuex 比较合适。

如果只是简单的多个组件间传递数据,可以使用组件间常用的通信方法。

Vuex 的设计思想

Vuex 借鉴了 Flux Redux The Elm Architecture ,将数据存放到全局的 store,再将 store 挂载到每个 vue 实例组件中,利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

Vuex 统一管理状态的好处

  • 能够在 Vuex 中集中管理共享的数据,易于开发和后期维护

  • 能够高效地实现组件之间的数据共享,提高开发效率

  • 存储在 Vuex 中的数据都是响应式的,能够实时保存数据与页面的同步




原文地址:访问原文地址
快照地址: 访问文章快照