vue实践04之vuex

1,186 阅读4分钟

vue实践04之vuex

vuex是一个专门为vue.js设计的集中式状态管理架构。我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。比如:我们有几个组件要显示用户名称和用户等级,或者显示用户的地理位置。如果我们不把这些属性设置为状态,那每个组件遇到后,都会到服务器进行查找计算,返回后再显示。在中大型项目中会有很多共用的数据,所以尤大神给我们提供了vuex。

vuex把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。

vuex示意图
注:图中虚线标注的区域即为vuex,可以看出vue components可以共享vuex。

适用场景

虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的global event bus参考链接 就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

开始

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

简单例子

  1. 引入vuex npm install vuex --save 需要注意的是这里一定要加上 --save,因为你这个包我们在生产环境中是要使用的。
  2. 新建一个vuex目录,在目录中新建store.js文件
    state 对应定义的状态值, mutations 内封装对状态值改变的函数。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
    state: {
        count: 1
    },
    mutations: {
        add(state) {
            state.count++;
        },
        reduce(state) {
            state.count--;
        }
    }
})
export default store
  1. 在components目录中新建count.vue文件
<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
 
            }
        },
        store
        
    }
</script>

注意上述代码中的两个store.commit事件。

  • 通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
  • 由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
  1. 在路由文件router/index.js文件中新增count的路由
 {
            path: '/count',
            component: Count
        }
  1. 访问页面http://127.0.0.1:8080/#/count
  2. 测试下vuex在不同component之间共享数据 在App.vue文件中同样也引用store,App.vue 代码如下:
<script>
import store from '@/vuex/store'
export default {
  name: "App",
   methods:{
    goback(){
      this.$router.go(-1);
    },
    goHome(){
      this.$router.push('/');
    }
  },
  // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  //(需调用 Vue.use(Vuex))
  store
};
</script>

注意上述代码中引入了store选项,这个选项可以把store的实例注入所有的子组件。 在App.vue文件template标签里增加显示state的代码如下:

<h3>{{$store.state.count}}</h3>

再次访问count页面,可以发现改变count组件的state值,App组件也会同步更新。

  1. 误区-组件共享

笔者试着同时打开两个count页面,天真以为改变一个页面的计数值,另一个页面也会自动变化,实际这是肯定不行的。vue就是一个单页应用,多页数据共享只能依赖与后端server的交互才能实现。vuex解决的时候单页中的多component共享数据的问题。

vue组件获得state

  1. 通过computed计算属性直接赋值
computed:{
            computeCount(){
             return this.$store.state.count;
          }
        }

显示调用代码为<h4>{{computeCount}}</h4> 完整代码如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
         <h4>{{computeCount}}</h4>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
 
            }
        },
        computed:{
            computeCount(){
             return this.$store.state.count;
          }
        },
        store
        
    }
</script>
  1. 通过mapState的对象来赋值
    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
  • 首先要用import引入mapState import {mapState} from 'vuex' -然后还在computed计算属性里写如下代码
computed: mapState({
    // 箭头函数可使代码更简练
    computeCount: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })

count2完整代码如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <h3>{{$store.state.count}}</h3>
         <h4>{{computeCount}}</h4>
         <h5>{{countAlias}}</h5>
         <h6>{{countPlusLocalState}}</h6>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    import {mapState} from 'vuex'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
                localCount:20
            }
        },
      computed: mapState({
    // 箭头函数可使代码更简练
    computeCount: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  }),
        store
        
    }
</script>
  1. 通过mapState的数组来赋值
    当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

完整代码如下:

<template>
    <div>
        <h2>{{msg}}</h2>
        <hr/>
        <!--<h3>{{$store.state.count}}</h3>-->
        <h6>{{count}}</h6>
        <div>
    <button @click="$store.commit('add')">+</button>
    <button @click="$store.commit('reduce')">-</button>
</div>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    import {mapState} from 'vuex'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
            }
        },
        computed: mapState([
            // 映射 this.count 为 store.state.count
          'count'
        ]),
        store
        
    }
</script>

参考资料

技术胖老师vuex精讲

vue官方文档