VueJs—状态管理组件Vuex
Posted VueJs技术分享
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VueJs—状态管理组件Vuex相关的知识,希望对你有一定的参考价值。
什么是Vuex
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official devtools extension to provide advanced features such as zero-config time-travel debugging and state snapshot export or import.
简单点一句话就是
"一个专门为 Vue.js
应用设计的数据统一管理和维护各个vue组件的可变化状态(你可以理解成vue
组件里的某些data
)架构"
我们为什么需要它?
当你的应用还很简单的时候,你可能并不需要 Vuex。我也并不建议在所有的场合都使用 Vuex。但是,如果你正在构建一个复杂的项目或者网站,你可能已经开始思考应该如何处理 Vue 组件之外的应用结构。这就是 Vuex 要解决的问题。
我们在用 Vue.js 的时候,通常会把状态储存在组件的内部。也就是说,每一个组件都拥有一部分状态,整个应用的状态是分散在多个地方的。然而我们经常遇到的一个需求就是需要把一部分的状态共享给其它组件。通常我们会使用 Vue 自带的$emit 或者$dispatch 或者props 或是第三方的事件系统去达到这个目的。但基于事件的问题在于,事件流会在逐渐增长的组件树中变得复杂,并且在出现问题的时候很难去发现问题的所在(典型的如事件 ping-pong,即子组件 dispatch 到父组件,再由父组件 broadcast)。
为了更好的解决在大型应用中共享状态的问题,我们需要把组件的本地状态 (component local state) 和应用状态 (application level state) 区分开来。应用级的状态不属于任何特定的组件,但每一个组件仍然可以通过 Vue 的响应系统去侦听其变化从而自动更新 DOM。通过把应用的状态管理逻辑放在同一个地方,我们就不需要在各个地方去处理各种事件,因为任何牵扯到多个组件的东西,都会放在这个地方。这样做也能使记录和检查状态的改变成为可能,甚至可以做出像 time-travel 这样的调试功能。
Vuex 也对状态管理的代码分离有一定程度的约束,但不会影响你实际代码结构的灵活性。
试想这样的场景,比如一个Vue根实例下面有一个根组件名为App.vue
,它下面有两个子组件A.vue
和B.vue
,父组件和子组件之间使用Props通讯是没问题的,通过绑定Props轻松的实现。
但是如果我们需要A.vue组件和B.vue组件之间通讯呢?因为组件实例的作用域是孤立的,它们之间是不能直接通讯的。那么只能借助共有的父组件通过自定义事件实现。
这只是一条通讯路径,那么如果父组件下有多个子组件,子组件之间通讯的路径就会变的很繁琐,父组件需要监听大量的事件,还需要负责分发给不同的子组件。很显然这并不是我们想要的组件化开发体验。
Vuex就是为了解决这一问题出现的。
Vuex是如何工作的?
这张图描述的很棒,完整的数据流闭环,整个应用的数据流是单向的。对我们理解Vuex和Vue的组件间的通讯关系很有帮助。
需要掌握的
(a) 用户在组件中的输入操作触发 action 调用
(b) Actions 通过分发 mutations 来修改 store 实例的状态
(c) Store 实例的状态变化反过来又通过 getters 被组件获知
使用Vuex
进入正题。一个购物车我们都需要完成哪些功能?先看看Demo的样子。
目录结构
需求分析
(a) 显示商品的文字描述、图片描述、类型、价格信息
(b) 改变商品颜色的时候图片切换
(c) 改变商品类型的时候价格变化
(d) 加入/移除购物车
(e) 购物车中商品数量统计以及总价的统计
我参考了中型到大型项目的目录结构说明构建的购物车,把Vuex相关的代码分割到多个模
配置vuex
每一个 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 } }
组件中通过getters读取状态
配合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里的。
组件不允许直接修改 store 实例的状态
在使用vuex的过程中你有可能会对从vuex.getters获取的数据进行再次操作。这是不允许的。改变 store 中的状态的唯一途径就是显式地分发
状态变更事件
组件永远都不应该直接改变 Vuex store 的状态。因为我们想要让状态的每次改变都很明确且可追踪,Vuex 状态的所有改变都必须在 store 的 mutation handler (变更句柄)中管理。
使用Vue Tools调试vuex
使用Vue Tools调试vuex是一个非常愉快的体验,可以在Components
中清楚的看到哪些数据是从vuex.getters中获取来的。
它记录了每次mutation的状态变化,保存了状态变化后的快照,我们可以定位到你想检查的快照观察数据的变化。
大功告成
以上是关于VueJs—状态管理组件Vuex的主要内容,如果未能解决你的问题,请参考以下文章