Vuex

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 借鉴了 FluxReduxThe Elm Architecture,将数据存放到全局的 store,再将 store 挂载到每个 vue 实例组件中,利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

Vuex 统一管理状态的好处

  • 能够在 Vuex 中集中管理共享的数据,易于开发和后期维护
  • 能够高效地实现组件之间的数据共享,提高开发效率
  • 存储在 Vuex 中的数据都是响应式的,能够实时保存数据与页面的同步
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容