在Vue中,当遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
Vuex通过把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
1、应用层级的状态应该集中到单个 store 对象中。
2、提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
3、异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
let store = new Vuex.Store({
strict: false, //启用严格模式,生产环境请置为false
modules: {
index: {
namespaced: true,
state: {
// 存放共享状态值
userInfo: localStorage.getItem("user") || {}, //用户信息
},
mutations: { //更新状态值
//userInfo
[INIT_USERINFO](state) {
state.userInfo = JSON.parse(localStorage.getItem("user")) || {};
},
},
actions: {
// 处理异步更新状态值
async initApp({ commit, dispatch }) {
commit(INIT_USERINFO);
await dispatch('initHome');
},
}
}
}
});
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法。但是mutation 必须是同步函数。
Action 类似于 mutation,不同在于: