应用结构

实际上,Vuex 在怎么组织你的代码结构上面没有任何限制,相反,它强制规定了一系列高级的原则:

  1. 应用级的状态集中放在 store 中。

  2. 改变状态的唯一方式是提交mutations,这是个同步的事务。

  3. 异步逻辑应该封装在action 中。

只要你遵循这些规则,怎么构建你的项目的结构就取决于你了。如果你的 store 文件非常大,仅仅拆分成 action、mutation 和 getter 多个文件即可。

对于稍微复杂点的应用,我们可能都需要用到模块。下面是一个简单的项目架构:

├── index.html
├── main.js
├── api
│   └── ... # 这里发起 API 请求
├── components
│   ├── App.vue
│   └── ...
└── store
├── index.js # 组合 modules 、export store
├── actions.js # 根 action
├── mutations.js # 根 mutations
└── modules
   ├── cart.js # cart 模块
   └── products.js # products 模块

关于更多,查看 购物车实例

Modules

由于使用了单一状态树,应用的所有状态都包含在一个大对象内。但是,随着我们应用规模的不断增长,这个Store变得非常臃肿。

为了解决这个问题,Vuex 允许我们把 store 分 module(模块)。每一个模块包含各自的状态、mutation、action 和 getter,甚至是嵌套模块, 如下就是它的组织方式:

const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
} const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
} const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
}) store.state.a // -> moduleA's state
store.state.b // -> moduleB's state

模块本地状态

模块的 mutations 和 getters方法第一个接收参数是模块的本地状态

const moduleA = {
state: { count: 0 },
mutations: {
increment: (state) {
// state 是模块本地的状态。
state.count++
}
}, getters: {
doubleCount (state) {
return state.count * 2
}
}
}

相似地,在模块的 actions 中,context.state 暴露的是本地状态, context.rootState暴露的才是根状态。

const moduleA = {
// ...
actions: {
incrementIfOdd ({ state, commit }) {
if (state.count % 2 === 1) {
commit('increment')
}
}
}
}

在模块的 getters 内,根状态也会作为第三个参数暴露。

const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}

命名空间

要注意,模块内的 actions、mutations 以及 getters 依然注册在全局命名空间内 —— 这就会让多个模块响应同一种 mutation/action 类型。你可以在模块的名称中加入前缀或者后缀来设定命名空间,从而避免命名冲突。如果你的 Vuex 模块是一个可复用的,执行环境也未知的,那你就应该这么干了。距离,我们想要创建一个 todos 模块:

// types.js

// 定义  getter、 action 和 mutation 的常量名称
// 并且在模块名称上加上 `todos` 前缀
export const DONE_COUNT = 'todos/DONE_COUNT'
export const FETCH_ALL = 'todos/FETCH_ALL'
export const TOGGLE_DONE = 'todos/TOGGLE_DONE'
// modules/todos.js
import * as types from '../types' // 用带前缀的名称来定义 getters, actions and mutations
const todosModule = {
state: { todos: [] }, getters: {
[types.DONE_COUNT] (state) {
// ...
}
}, actions: {
[types.FETCH_ALL] (context, payload) {
// ...
}
}, mutations: {
[types.TOGGLE_DONE] (state, payload) {
// ...
}
}
}

注册动态模块

你可以用 store.registerModule 方法在 store 创建之后注册一个模块:

store.registerModule('myModule', {
// ...
})

模块的 store.state.myModule 暴露为模块的状态。

其他的 Vue 插件可以为应用的 store 附加一个模块,然后通过动态注册就可以使用 Vuex 的状态管理功能了。例如,vuex-router-sync 库,通过在一个动态注册的模块中管理应用的路由状态,从而将 vue-router 和 vuex 集成。

你也能用 store.unregisterModule(moduleName) 移除动态注册过的模块。但是你不能用这个方法移除静态的模块(也就是在 store 创建的时候声明的模块)。

Plugins

Vuex 的 store 接收 plugins 选项,这个选项暴露出每个 mutation 的钩子。一个 Vuex 的插件就是一个简单的方法,接收 sotre 作为唯一参数:

const myPlugin = store => {
// 当 store 在被初始化完成时被调用
store.subscribe((mutation, state) => {
// mutation 之后被调用
// mutation 的格式为 {type, payload}。
})
}

然后像这样使用:

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

在插件内提交 Mutations

插件不能直接修改状态 - 这就像你的组件,它们只能被 mutations 来触发改变。

通过提交 mutations,插件可以用来同步数据源到 store。例如, 为了同步 websocket 数据源到 store (这只是为说明用法的例子,在实际中,createPlugin 方法会附加更多的可选项,来完成复杂的任务)。

export default function createWebSocketPlugin (socket) {
return store => {
socket.on('data', data => {
store.commit('receiveData', data)
})
store.subscribe(mutation => {
if (mutation.type === 'UPDATE_DATA') {
socket.emit('update', mutation.payload)
}
})
}
}
const plugin = createWebSocketPlugin(socket)

const store = new Vuex.Store({
state,
mutations,
plugins: [plugin]
})

生成状态快照

有时候插件想获取状态 “快照” 和状态的改变前后的变化。为了实现这些功能,需要对状态对象进行深拷贝:

const myPluginWithSnapshot = store => {
let prevState = _.cloneDeep(store.state)
store.subscribe((mutation, state) => {
let nextState = _.cloneDeep(state) // 对比 prevState 和 nextState... // 保存状态,用于下一次 mutation
prevState = nextState
})
}

** 生成状态快照的插件只能在开发阶段使用,使用 Webpack 或 Browserify,让构建工具帮我们处理:

const store = new Vuex.Store({
// ...
plugins: process.env.NODE_ENV !== 'production'
? [myPluginWithSnapshot]
: []
})

插件默认会被起用。为了发布产品,你需要用 Webpack 的 DefinePlugin 或者 Browserify 的 envify 来转换 process.env.NODE_ENV !== 'production' 的值为 false

内置 Logger 插件

如果你正在使用 vue-devtools,你可能不需要。

Vuex 带来一个日志插件用于一般的调试:

import createLogger from 'vuex/dist/logger'

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

createLogger 方法有几个配置项:

const logger = createLogger({
collapsed: false, // 自动展开记录 mutation
transformer (state) {
// 在记录之前前进行转换
// 例如,只返回指定的子树
return state.subTree
},
mutationTransformer (mutation) {
// mutation 格式 { type, payload }
// 我们可以按照想要的方式进行格式化
return mutation.type
}
})

日志插件还可以直接通过 <script> 标签, 然后它会提供全局方法 createVuexLogger

要注意,logger 插件会生成状态快照,所以仅在开发环境使用。

严格模式

要启用严格模式,只需在创建 Vuex store 的时候简单地传入 strict: true

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

在严格模式下,只要 Vuex 状态在 mutation 方法外被修改就会抛出错误。这确保了所有状态修改都会明确的被调试工具跟踪。

开发阶段 vs. 发布阶段

  • 不要在发布阶段开启严格模式! 严格模式会对状态树进行深度监测来检测不合适的修改 —— 确保在发布阶段关闭它避免性能损耗。

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

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

相关引用

关于Vue.js 2.0 的 Vuex 2.0,你需要更新的知识库的更多相关文章

  1. vue.js和vue-router和vuex快速上手知识

    vue.js和vue-router和vuex快速上手知识 一直以来,认为vue相比react而言,学习成本会更低,会更简单,但最近真正接触后,发现vue的各方面都有做一些客户化的优化,有一些亮点,但也 ...

  2. Vue.js——十分钟入门Vuex

    一. 什么是Vuex?   Vuex Vuex是一个专门为Vue.js应用程序开发的状态管理模式, 它采用集中式存储管理所有组件的公共状态, 并以相应的规则保证状态以一种可预测的方式发生变化. Vue ...

  3. Vue.js状态管理模式 Vuex

    vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 安装.使用 vuex 首先我们在 vue. ...

  4. [Nuxt] Build a Vue.js Form then use Vuex Actions to Post to an API in Nuxt

    The default behavior of submitting an HTML form is to reload the page. You can use the Vue.js @submi ...

  5. Vue.js中学习使用Vuex详解

    在SPA单页面组件的开发中 Vue的vuex和React的Redux 都统称为同一状态管理,个人的理解是全局状态管理更合适:简单的理解就是你在state中定义了一个数据之后,你可以在所在项目中的任何一 ...

  6. Vue.js vui 饿了么Vue2.0的组件库

    http://www.oschina.net/news/78038/vue-js-2-0-3 http://git.oschina.net/durcframework/vui http://eleme ...

  7. [Nuxt] Update Vuex State with Mutations and MapMutations in Vue.js

    You commit changes to state in Vuex using defined mutations. You can easily access these state mutat ...

  8. vue.js组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然 ...

  9. VUE.JS组件化

    VUE.JS组件化 前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎 ...

随机推荐

  1. [APUE]进程控制(上)

    一.进程标识 进程ID 0是调度进程,常常被称为交换进程(swapper).该进程并不执行任何磁盘上的程序--它是内核的一部分,因此也被称为系统进程.进程ID 1是init进程,在自举(bootstr ...

  2. .NET Core系列 :3 、使用多个项目

    通过前面的两篇文章,我们已经知道如何创建新的项目,如何生成并运行我们的应用程序,也知道(大致) project.json 文件中的内容是什么意思.但大多数项目往往也需要多个项目或引用的类库.我们要创建 ...

  3. 用CIL写程序:你好,沃尔德

    前言: 项目紧赶慢赶总算在年前有了一些成绩,所以沉寂了几周之后,小匹夫也终于有时间写点东西了.以前匹夫写过一篇文章,对CIL做了一个简单地介绍,不过不知道各位看官看的是否过瘾,至少小匹夫觉得很不过瘾. ...

  4. 基于Oracle安装Zabbix

    软件版本 Oracle Enterprise Linux 7.1 64bit Oracle Enterprise Edition 12.1.0.2 64bit Zabbix 3.2.1 准备工作 上传 ...

  5. junit4进行单元测试

    一.前言 提供服务的时候,为了保证服务的正确性,有时候需要编写测试类验证其正确性和可用性.以前的做法都是自己简单写一个控制层,然后在控制层里调用服务并测试,这样做虽然能够达到测试的目的,但是太不专业了 ...

  6. Linux之搭建自己的根文件系统

    Hi!大家好,我是CrazyCatJack.又和大家见面了.今天给大家带来的是构建Linux下的根文件系统.希望大家看过之后都能构建出符合自己需求的根文件系统^_^ 1.内容概述 1.构造过程 今天给 ...

  7. Entity Framework 教程——Entity Framework中的实体类型

    Entity Framework中的实体类型 : 在之前的章节中我们介绍过从已有的数据库中创建EDM,它包含数据库中每个表所对应的实体.在EF 5.0/6.0中,存在POCO 实体和动态代理实体两种. ...

  8. CommandPattern

    /** * 命令模式 * @author TMAC-J * 将调用者和接受者分离 * 可以将一组命令组合在一起,适合很多命令的时候 */ public class CommandPattern { i ...

  9. Java Spring mvc 操作 Redis 及 Redis 集群

    本文原创,转载请注明:http://www.cnblogs.com/fengzheng/p/5941953.html 关于 Redis 集群搭建可以参考我的另一篇文章 Redis集群搭建与简单使用 R ...

  10. 不要听吹牛逼什么前端MVVM框架就是好,其实都是一帮没学好分层设计的搞出来的,让你彻底看清前端MVVM的本质

    最近前端圈子里面,发现大家都在热炒概念,什么knockout,angularJs,都被捧成神了,鄙人不才,最近心情也不好,特地写这篇文章来找骂 写代码的码农都知道,Java社区虽然不是一个提出分层思想 ...