11

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.

可以理解为,Vuex 为 VUE 提供一套通过状态共享实现的组件通信方案。

Vuex 与其他通信方案

props down 和 events up

父组件通过 props 向子组件传值,并且监听子组件的自定义事件。

首选通信方式,因为它是一种低耦合的方式。但在特定场景下有几个不足:

  • 它是一个典型的单向数据流,父组件不能主动触发子组件的事件

  • 非父子组件中通信,可以通过父组件做桥梁,但是这样父组件容易变得很臃肿,被高度耦合

dispatch 和 broadcast

在 Vue 1.0 中用来解决上面第一点不足

在 Vue 2.0 已经废弃,不建议使用。在特定场景下有几点不足:

  • 传播效率比较低,组件树越深,枝叶越多,效率也会越低

  • 传播方向单一,实现非父子关系的组件通信代价太大

event bus

Vue 2.0 废弃 dispatch 和 broadcast 时,建议使用event bus 作为一种替代方案。

它是经典的发布订阅模式。通过 Vue 实现十分简单(当然,自己实现也很容易):


// 创建消息中心
const bus = new Vue()

// 假设调用双方已经约定一个名为 myEvent 消息以及传值方式

// 订阅
bus.$on('myEvent',value=>{
    alert(value)
})

// 发布
bus.$emit('myEvent','ok')

它是一种完全基于事件的方式来传值,在特定场景下有点不足:

  • 消息容易重名

    为了减少消息越来越多,导致消息重名,应该根据不同的通信范围,依赖独立的消息中心,各自管理各自的消息,减少影响范围。
    
  • Multiple views may depend on the same piece of state.

    
    操作重复且冗余,同一个状态被分散在不同地方,不方便管理。
    

共享引用

<div>{{shareData.a}}</div>
<c1></c1>
<c2></c2>

    const obj = {a: 1, b: 2}
    
    new Vue({
        el: 'body',
        data:{
            shareData:obj
        },
        compoments:{
            c1:{
                template:`
                    <div>{{shareData.a}}</div>
                `,
                data(){
                    return {
                        myData:1,
                        shareData:obj
                    }
                }
            },
            c2:{
                template:`
                    <div>{{shareData.a}}</div>
                `,
                data(){
                    return {
                        myData:2,
                        shareData:obj
                    }
                }
            }
        }
    })

父组件通过 props 传一个引用类型给子组件也可以达到同样的效果。

它像是裸奔的Vuex,只共享了状态,在复杂的场景下难以预测状态在什么情况下会发生变化,导致追踪问题困难、维护成本增加。

Rx

有待研究

Flux Redux

和 Vuex 的原理相近,不作讨论

Vuex

基于以上的一些不足,Vuex 正好可以用来解决那些痛点。

那么 Vuex 有哪些不足呢?

它是基于数据共享的,那么显而易见,共享的越多,代码的耦合度约高,这就是在使用Vuex的时候需要去平衡的地方。

开发中的疑惑

  • 在什么场景下建议使用 Vuex ?

  • 引入 Vuex 后的会有哪些变化?

  • Vuex 的版本如何选择?

  • 如何区分和理解 actions 和 mutations 的使用场景?

  • 如何定义 state ?

在什么场景下建议使用 Vuex

通过以上的分析,可以得出一个大致的结论,Vuex 解决了其他方案没有解决的问题,其实也正好是它的应用场景 —— 两个以上组件共享数据。

官方文档这样描述了这样一个场景

Multiple views may depend on the same piece of state.

Actions from different views may need to mutate the same piece of state.

官方文档所描述的场景多组件共享数据的情况。

如果是两个非父子组件之间完全可以用 event bus 实现。

如果应用中有一些是两个以上的组件共享数据,有些是两个非父子组件共享数据,那么,是全部用Vuex 还是 Vuex 和 event bus 混合用呢?

如果是三个组件共享数据,而又有一个很多表单交互,是否值得为双向绑定,增加额外的代码量?

引入 Vuex 后的会有哪些变化?

牺牲掉的灵活性

Vue 最经典的一个例子,莫过于在表单元素上做的双向绑定
而 Vuex 核心概念是单向数据流,必须遵循

Vuex 给我们带来一套数据管理解决方案的,同时在一定程度上牺牲掉一部分灵活性,增加了代码量。

业务组件 通用组件

简单的可以认为,通用组件不应该和 Vuex 关联起来,只通过 prop event 通信

Vuex 的版本如何选择?

出于以下几点,最终选择了 Vuex 2.0

版本兼容性

当前我正在使用 Vue 1.0.28 开发项目,下意识的认为 Vuex 应该和 Vue-router 一样:

This is vue-router 2.0 which works only with Vue 2.0. For the 1.x router see the 1.0 branch.

在官方资料中也没有找到相关说明提示,最后,做了一个 demo 加入 Vuex 2.0.0 和 Vue 1.0.28 一起运行,测试结果没有发现跨版本的兼容问题。

排除了兼容问题。

版本变化

理念还是单向数据流,
2.0 中有几个很重要的变化,使得代码结构变得更清晰:

  • 通过 store.dispatch 触发 actions

    妈妈再也不用担心我在 `methods` 中定义的方法名和 `actions` 中的重名了
    

(略了,有空补充)

如何区分和理解 actions 和 mutations 的使用场景?

可以看看知乎上作者的回答

区分 actions 和 mutations 并不是为了解决竞态问题,而是为了能用 devtools 追踪状态变化。

事实上在 vuex 里面 actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发 mutation 就行。异步竞态怎么处理那是用户自己的事情。vuex 真正限制你的只有 mutation 必须是同步的这一点(在 redux 里面就好像 reducer 必须同步返回下一个状态一样)。

既然设计出来,我的理解可以这样使用:

  • 异步操作天然要定义成 action

  • 可以把 mutation 看作单一职责,actions 是 mutation 的组合体

    也就是说,如果多个函数对 state 做了同样的操作,应该把这些操作提取出来,放到 mutation 中,这些函数定义成 action
    

如何定义state?

// a 模块

const a =[{
    id:1,
    isShow:true,
    aName:'a1'
},{
    id:2,
    isShow:false,
    aName:'a2'
}];


// b 模块

const b =[{
    id:1,
    isShow:true,
    bName:'b1'
},{
    id:2,
    isShow:false,
    bName:'b2'
}];

// c 模块

const b =[{
    id:1,
    isShow:true,
    cName:'c1'
},{
    id:2,
    isShow:false,
    cName:'c2'
}];

三个模块对应 id 同步 isShow 的状态,那么问题来了 state 应该怎么定义?


// 方案1

const a= null;

const b= null;

const c= null;

const state =[{
    id:1,
    isShow:true,
    aName:'a1',
    bName:'b1',
    cName:'c1'
},{
    id:2,
    isShow:false,
    aName:'a1',
    bName:'b2',
    cName:'c2'
}]

// 方案2

const a =[{
    id:1,
    aName:'a1'
},{
    id:2,
    aName:'a2'
}];

const b =[{
    id:1,
    bName:'b1'
},{
    id:2,
    bName:'b2'
}];

const c =[{
    id:1,
    cName:'c1'
},{
    id:2,
    cName:'c2'
}];

const state =[{
    id:1,
    isShow:true
},{
    id:2,
    isShow:false
}]

我会选择方案2,理由是共享该共享的,不应该共享多余的数据。

但是纠结的是方案1,维护性看上去更好,而扩展起来没有方案2灵活。

入手学习

Vuex 历经了 0.x 、 1.x(没有出正式版直接跳2.0了) 到现在的2.0正式版,不说翻天覆地,却也是变化相当大。

先看了几遍 1.0 的中文文档,把单项数据流等一些概念简单学习,把 Vuex 想干什么,能干什么以及一些理念了解下,再简单写了个例子就算是入门了。

要注意的是,中文文档不是作者翻译的,而且后续的巨大的变更没有及时更新,当然,用它做一份中文入门资料还是不错的。

有了之前的入门基础后,可以更好理解的英文文档的一些概念,阅读起来不会太吃力。

当然,在知乎上有一些作者关于 Vuex 的回答,对理解 Vuex 有一定的帮助。

题外话

感觉 avalon = Vue + Vuex。


siwuxie
528 声望67 粉丝

404