[Vue.js启航]——使用Vuex进行状态管理

Vue.js启航——英雄编辑器(五)

简介

 这一篇博客将是这一系列博客的最终篇,主要探讨的是Vuex插件的使用。先介绍一下Vuex。Vuex是Vue框架的状态管理插件,采用集中式存储管理应用的所有组件的状态。状态通俗来讲就是组件中的数据,状态的管理也就是各个组件中数据的管理。当你的应用足够简单的话,我们可以使用中央事件总线的方式来实现简单的管理,具体可以参考 [Vue.js启航]——多组件应用构建 ,当我们要构建中大型的单页应用的时候我们才会考虑使用Vuex来集中管理组件的状态。这篇文章我们会继续 [Vue.js启航]——多组件应用构建,去掉中央事件总线,采用Vuex进行改造。

使用Vuex的多组件英雄编辑器

 使用Vuex的多组件英雄编辑器和使用时间总线的多组件英雄编辑器实现出来的效果是一样的

主从结构的英雄编辑器

最主要的改造就是将bus换成Vuex.Store实例,将heroVue实例中的computed选项作一些调整。

Vuex.Store实例

 跟中央时间总线一样,使用Vuex管理状态也需要一个额外的组件实例,在中央时间总线中是一个Vue实例,而在Vuex中则是Vuex.Store实例。最简单的Vuex.Store应该包括statemutations选项,其中state选项存储的是需要管理的状态,而mutations则是控制状态转变的一些方法,例如在官方指南中,mutations中有一个改变statecount的方法

var store=new Vuex.Store({
    state:..
    mutations:{
        increment (state){
            state.count++;
        }
    }
})

当我们的组件需要使用mutations的方法更改state的状态时只需要调用

store.commit('increment')

使用载荷传递参数

在我们的例子中需要将选中的英雄作为参数传递给mutations中的方法。在这里就直接拿我们的代码举例子了,使用载荷的mutations的写法是这样的

//...
mutations:{
    selectHero(state,selectedHero){
        state.hero=selectedHero
    }
}

在英雄列表中的调用方式是

store.commit('selectHero',this.selectedHero)

但这并不是官方推荐的写法,官方的规范中要求,载荷应该是一个对象,这样可以包含多个字段并且记录的mutation更加易读,我们继续使用载荷对象进行改造

//...
mutations:{
    selectHero(state,payload){
        state.hero=payload.selectedHero;
    }
}

然后在英雄列表中的调用方式是

store.commit({
    type:'selectHero',
    selectedHero:this.selectedHero
})

组件或者实例获取状态

 当我们把Vuex.Stroe实例编写好之后,已经把状态和改变状态的方法都集中起来进行管理了,那接下来我们就要获取状态的值了。获取状态值的一般方法是return store.state.xxxxxx为状态值,例如我们在这里需要获取hero的状态值就写return sotre.state.hero,在这个例子中,我们只需要在hero实例中的computed选项中返回状态值就好

//..
computed:{
    hero:function(){
        return store.state.hero;
    }
}

到这里我们就完成了从中央事件总线到Vuex的转变。

模块化的Sotre

 其实一些有经验的人到这里可能就会问,我们把所有的组件的状态都交由一个Store管理,那么,当我们要管理的状态很多,那Store不就会膨胀了吗?其实Vuex的设计者早就想到了这一点,所以Vuex允许使用模块化管理Store。每一个模块其实就是拥有statemutations,actions,getters中的一个或多个选项的JS对象,这里我们只使用了statemutations。构建模块化Store的代码如下:

var moduleA={
    state:{...},
    mutations:{...},
    ...
}

var moduleB={
    state:{...},
    mutations:{...},
    ...
}

然后在Store实例中将各个模块组装起来

var store=new Vuex.Store({
    modules:{
        a:moduleA,
        b:moduleB
    }
})

获取状态的话使用

store.state.a.state.xxxx

总结

 这篇文章主要是介绍了Vuex的简单使用,配合Vue-Router,和Vue本身组成了Vue的核心部分。Vuex当然不止文章中用到的statemutations还有包含异步操作的actions以及用于筛选数据或者格式化输出的getters。模块化系统还有命名空间等内容。在这里接触到关于Vuex的内容其实差不多,就不多详述了。
这一系列的文章也就结束了,接下来会新开一系列的文章,主要关于模块化构建系统构建Vue应用,使用单文件组件,和Vue-Router、Vuex的进阶使用和Vue的核心概念等。

可运行代码

hero.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="hero.css">
</head>
<body>

    <div id="hero">
        <hero-list v-bind:heroes="heroes" 
            v-bind:title="title"></hero-list>
        <hero-detail v-bind:hero="hero"></hero-detail>
    </div>

</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@2.3.1"></script>
<script src="hero.js"></script>
</html>

hero.js
//十位英雄数据
const HEROES=[
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];

//Vuex.Store实例
var store=new Vuex.Store({
    state:{
        hero:""
    },
    mutations:{
        selectHero(state,payload){
            state.hero=payload.selectedHero;
        }
    }
})

//多个组件

//英雄详情组件
Vue.component("hero-detail",{
    props:["hero"],
    template:'\
    <div v-if="hero">\
        <h2>{{hero.name}}</h2>\
        <div><label>id:</label>{{hero.id}}</div>\
        <div>\
            <label>name:</label>\
            <input v-model="hero.name" placeholder="name"/>\
        </div>\
    </div>\
    ',
})

//英雄列表组件
Vue.component("hero-list",{
    props:["title","heroes"],
    template:'\
    <div>\
        <h1>{{title}}</h1>\
        <h2>My Heroes</h2>\
        <ul class="heroes">\
            <li v-for="hero in heroes"\
                v-on:click="onSelect(hero)"\
                v-bind:class="{selected:hero===selectedHero}">\
                <span class="badge">{{hero.id}}</span>{{hero.name}}\
            </li>\
        </ul>\
    </div> \
    ',
    methods:{
        onSelect:function(hero){
            this.selectedHero=hero
            //bus.$emit("selectHero",this.selectedHero);
            store.commit({
                type:'selectHero',
                selectedHero:this.selectedHero
            })
        }
    },
    data:function(){
        return {selectedHero:""};
    }
})



var hero=new Vue({
    el:"#hero",
    data:{
        heroes:HEROES,
        title:"Tour of Heroes",
    },
    computed:{
        hero:function(){
            return store.state.hero;
        }
    },
})

hero.css

h1 {
    color: #369;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 250%;
}
h2, h3 {
    color: #444;
    font-family: Arial, Helvetica, sans-serif;
    font-weight: lighter;
}
.selected {
    background-color: #CFD8DC !important;
    color: white;
}
body {
    margin: 2em;
}
body, input[text] {
    color: #888;
    font-family: Cambria, Georgia;
}
.heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
}
.heroes li {
    cursor: pointer;
    position: relative;
    left: 0;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
}
.heroes li.selected:hover {
    background-color: #BBD8DC !important;
    color: white;
}
.heroes li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
}
.heroes .text {
    position: relative;
    top: -3px;
}
.heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color: #607D8B;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
}
[class*='col-'] {
    float: left;
    padding-right: 20px;
    padding-bottom: 20px;
}
[class*='col-']:last-of-type {
    padding-right: 0;
}
a {
    text-decoration: none;
}
h3 {
    text-align: center; margin-bottom: 0;
}
h4 {
    position: relative;
}
.grid {
    margin: 0;
}
.col-1-4 {
    width: 23%;
}
.module {
    padding: 20px;
    text-align: center;
    color: #eee;
    max-height: 120px;
    min-width: 120px;
    background-color: #607D8B;
    border-radius: 2px;
}
.module:hover {
    background-color: #EEE;
    cursor: pointer;
    color: #607d8b;
}
.grid-pad {
    padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
    padding-right: 20px;
}
@media (max-width: 600px) {
.module {
    font-size: 10px;
    max-height: 75px; }
}
@media (max-width: 1024px) {
.grid {
    margin: 0;
}
.module {
    min-width: 60px;
}
}
nav a {
    padding: 5px 10px;
    text-decoration: none;
    margin-top: 10px;
    display: inline-block;
    background-color: #eee;
    border-radius: 4px;
}
nav a:visited, a:link {
    color: #607D8B;
}
nav a:hover {
    color: #039be5;
    background-color: #CFD8DC;
}
nav a.router-link-active {
    color: #039be5;
}
button {
    margin-top: 20px;
    font-family: Arial;
    background-color: #eee;
    color: #888888; 
    border: none;
    padding: 5px 10px;
    border-radius: 4px;
    cursor: pointer; cursor: hand;
}
button:hover {
    background-color: #cfd8dc;
}
button:disabled {
    background-color: #eee;
    color: #ccc; 
    cursor: auto;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若即

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值