应用结构

实际上,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. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  2. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  3. 探真无阻塞加载javascript脚本技术,我们会发现很多意想不到的秘密

    下面的图片是我使用firefox和chrome浏览百度首页时候记录的http请求 下面是firefox: 下面是chrome: 在浏览百度首页前我都将浏览器的缓存全部清理掉,让这个场景最接近第一次访问 ...

  4. 从零开始编写自己的C#框架(24)——测试

    导航 1.前言 2.不堪回首的开发往事 3.测试推动开发的成长——将Bug消灭在自测中 4.关于软件测试 5.制定测试计划 6.编写测试用例 7.执行测试用例 8.发现并提交Bug 9.开发人员修复B ...

  5. Django

    一.Django 简介 Django 是一个由 Python 写成的开放源代码的 Web 应用框架.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是 CMS(内容管理系统) ...

  6. [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化

    KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...

  7. java中易错点(一)

    由于replaceAll方法的第一个参数是一个正则表达式,而"."在正则表达式中表示任何字符,所以会把前面字符串的所有字符都替换成"/".如果想替换的只是&qu ...

  8. 使用HTML5的cavas实现的一个画板

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta http-e ...

  9. Atitit.研发管理软件公司的软资产列表指南

    Atitit.研发管理软件公司的软资产列表指南 1. Isv模型下的软资产1 2. 实现层面implet1 3. 规范spec层1 4. 法则定律等val层的总结2 1. Isv模型下的软资产 Sof ...

  10. CSS笔记

    初级篇===========================选择器============================元素选择器css:h1{color: red}html:<h1> ...