vue-demo-collection已升级为2.x版本,
本文是基于vue全家桶1.x和webpack1.x写的demo,
查看2.x源码移步shopping-cart2.x,
查看1.x和2.x代码对比移步shopping-cart源码比较。javascript
本文是上篇文章的序章,一直想有机会再次实践下Vuex。写下这篇总结,See Demo。html
Flux-inspired Application Architecture for Vue.jsvue
Vuex其实是类Flux的数据管理架构。它主要帮咱们更好的组织代码,更好的让Vue中的状态更好的经过状态管理维护起来。在实际项目运用中咱们须要对组件的 组件本地状态(component local state)
和 应用层级状态(application level state)
进行区分。
Vuex的做用就是聚集应用层级的状态到一处,方便管理。java
说状态其实有些同窗可能不太理解,那么在刚上手Vue的时候作过的例子中,一个Vue实例中都会有data,那么这个data中保存的属性其实就是能够理解为状态。webpack
试想这样的场景,好比一个Vue根实例下面有一个根组件名为App.vue
,它下面有两个子组件A.vue
和B.vue
,父组件和子组件之间使用Props通信是没问题的,经过绑定Props轻松的实现。git
可是若是咱们须要A.vue组件和B.vue组件之间通信呢?由于组件实例的做用域是孤立的,它们之间是不能直接通信的。那么只能借助共有的父组件经过自定义事件实现。github
A组件想要和B组件通信每每是这样的:web
A小弟说:“报告老大,可否帮我捎个信给你的小弟B组件?”,它须要dispatch
一个事件给Appvuex
App老大说:“包在我身上”,它须要监听A组件dispatch
的事件,同时须要broadcast
一个事件给B组件。架构
B小弟说:“信息已收到。”,它须要on
监听App组件分发的事件。
这只是一条通信路径,那么若是父组件下有多个子组件,子组件之间通信的路径就会变的很繁琐,父组件须要监听大量的事件,还须要负责分发给不一样的子组件。很显然这并非咱们想要的组件化开发体验。
Vuex就是为了解决这一问题出现的。
下面这张图很好的诠释了Vuex和组件之间的通信关系。
这张图描述的很棒,完整的数据流闭环,整个应用的数据流是单向的。对咱们理解Vuex和Vue的组件间的通信关系颇有帮助。
须要掌握的:
用户在组件中的输入操做触发 action 调用;
Actions 经过分发 mutations 来修改 store 实例的状态;
Store 实例的状态变化反过来又经过 getters 被组件获知。
进入正题。一个购物车咱们都须要完成哪些功能?先看看Demo的样子。
显示商品的文字描述、图片描述、类型、价格信息;
改变商品颜色的时候图片切换;
改变商品类型的时候价格变化;
加入/移除购物车;
购物车中商品数量统计以及总价的统计;
我参考了中型到大型项目的目录结构说明构建的购物车,把Vuex相关的代码分割到多个模块(module),我认为这样更清晰明了。
每个 Vuex 应用的核心就是 store(仓库)。"store" 基本上就是一个容器,它包含着你应用里大部分的 状态(即 state),咱们建立store.js
导入各个模块的初始状态和 mutations。
store.js
// vuex/store.js import Vue from 'vue' import Vuex from 'vuex' import index from './modules/index' Vue.use(Vuex) export default new Vuex.Store({ // 组合各个模块 modules: { index } })
经过在根实例中注册 store 选项,该 store 实例会注入到根组件下的全部子组件中。
App.vue
import Nav from './components/Nav.vue' import store from './vuex/store' export default { name: 'App', store, data() { return { // note: changing this line won't causes changes // with hot-reload because the reloaded component // preserves its current state and we are modifying // its initial state. } }, components: { 'cart-nav': Nav } }
配合ES6的箭头函数真的很简洁的。
vuex: { getters: { iPhone6S: ({ index }) => index.iPhone6S } }
好比咱们须要根据更改外观来改变商品的图片这样的需求。咱们该怎样完成一个数据流闭环呢?
从组件开始:
一(Vue components
)、首先子组件Index.vue
须要一个点击事件来触发action:
Index.vue
<li v-for="styleUrl in iPhone6S.style" @click="changeStyle($key, styleUrl)" :class="{active: iPhone6S.activeStyleUrl == styleUrl}"><span v-text="$key"></span></li>
二(Actions
)、声明一个名为changeStyle
的action
actions.js
export const changeStyle = makeAction('CHANGE_STYLE')
并分发mutations,用统一的函数处理。
function makeAction (type) { return ({ dispatch }, ...args) => dispatch(type, ...args) }
三(Mutations
)、咱们用常量声明mutation,并把它放到单独的地方。mutation常量习惯性大写的,区分于actions。
mutation-types.js
export const CHANGE_STYLE = 'CHANGE_STYLE'
四(State
)、在模块中导入mutation改变状态:
index.js
[CHANGE_STYLE] (state, styleName, styleUrl) { state.iPhone6S.activeStyle = styleName state.iPhone6S.activeStyleUrl = styleUrl }
因为 Vuex store 内部的 state 对象被 Vue 改形成了响应式对象,当咱们对 state 进行修改时,任何观测着 state 的 Vue 组件都会自动地进行相应地更新。
使用Vuex管理状态并非须要把全部的状态都放在Vuex里。如上所述,组件本地状态是不须要写在Vuex里的。
在使用vuex的过程当中你有可能会对从vuex.getters获取的数据进行再次操做。这是不容许的。改变 store 中的状态的惟一途径就是显式地分发 状态变动事件
组件永远都不该该直接改变 Vuex store 的状态。由于咱们想要让状态的每次改变都很明确且可追踪,Vuex 状态的全部改变都必须在 store 的 mutation handler (变动句柄)中管理。
使用Vue Tools调试vuex是一个很是愉快的体验,能够在Components
中清楚的看到哪些数据是从vuex.getters中获取来的。
它记录了每次mutation的状态变化,保存了状态变化后的快照,咱们能够定位到你想检查的快照观察数据的变化。