谈一谈我是怎么学习使用vuex的

好久没有写文章了,感觉自己就这样稀里糊涂的过了好久了。最近在研究小程序 本来想等小程序有所得之后再来写点什么。但是发现自己研究来研究去 ,感觉没没有什么好写的。但是又不想丢弃掉自己写作的一个习惯。刚好前段时间在重构代码的时候,发现了之前封装代码的时候出现了很多的奇怪的写法,和很多之前没考虑到的漏洞,多少还是有点心得的,写下这个文章呢?和大家交流一下

一、 为什么要用vuex

首先我们谈到一个技术肯定是要从以下几个方面来进行探究的:

  • 什么情况下要用这项技术
  • 这项技术相对于其他技术的优势

我这里呢?就从这两个方面来开始,逐次、深层次的介绍vuex。

首先现今几大主流的框架:react,vue,angluar包括小程序,无以复加的都提出一个组件化的思想。这样做的好处是:高内聚、可重用、可互换、可组合等优势,但同时也在开发中带来了许多新的问题,其中最重大的研究课题是:组件间的通信

前端也差不多开发了一年半的时间了,主流的几种vue的组件间传递信息的方式,无非有以下几种:
1、 通过 $emitprops在父子组件中进行数据传递。
2、 用eventbus(全局事件)在中进行传递。
3、 利用localstorage、sessionstorage等存储手段来进行传递。
毫无疑问,这些个方式都能完成我们所要的需求。但是在某种程度上面或多或少都有一些问题。例如:
1、 利用$emitprops在兄弟关系嵌套比较深的情况之下,代码书写量将会大大的增加。
2、 全局事件在使用的时候可能被被多次触发、销毁时机掌握等等一系列的问题,同时作为全局事件所有页面都能监听、掌控事件可能会存在安全性问题等等...
3、 ...

既然前面说了这么多其他技术的坏话,现在就要来介绍一下今天文章的主角了。
vuex--是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

首先来明确几个概念:
一、状态管理模式


上面是一个vue简单的状态管理应用,其中应该包含有:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

当然上面只是一个最简单的“单向数据流”的示意图。但是当我们存在很多的组件嵌套的时候,可能会存在这样几个问题:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

显然用前面的哪几个技术可能会将页面的状态变得更加复杂和代码逻辑繁琐,不利于代码的维护和深层次开发。在这种大环境下面,设计师们专门为vuex设计了一款状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。



上面便是vuex的工作流程,下面我们就开始简单的来介绍一下vuex的一些构成。

二、 vuex的简单介绍

说到vuex的介绍呢?我们就先从代码的引入开始:

npm install vuex --save
import Vue from "vue";
import Vuex from "vuex"

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions
})

最后再main.js中:

import store from "./store"
var app=new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

看了上面的代码,首先来解释一下:new Vuex.Store({}) 表示创建一个Vuex实例,通常情况下,他需要注入到Vue实例里. Store是Vuex的一个核心方法,字面上理解为“仓库”的意思。Vuex Store是响应式的,当Vue组件从store中读取状态(state选项)时,若store中的状态发生更新时,他会及时的响应给其他的组件(类似双向数据绑定) 而且不能直接改变store的状态,改变状态的唯一方法就是,显式地提交更改(mutations选项)

上面就将vuex的四个核心选项:state mutations getters actions说了出来。

2.1、 state:

首先是state,如何来获取state的值呢?一般是将这个值放置在computer里面,这样的话一旦数据发生改变的时候,就反馈到页面上面去。代码如下:

// state_name:klivitam
<template>
  <div>
    {{username}}
  </div>
</template>

<script>
  export default {
    computed:{
      username(){
        return this.$store.state.name;
      }
    }
  }
</script>

state总结:用来存放组件之间共享的数据。他跟组件的data选项类似,只不过data选项是用来存放组件的私有数据。

2.2、 getters:

其次是getters,来看下面一段代码:
vuex里面的代码

import Vue from "vue";
import Vuex from "vuex"

let state = {
  name:"klivitam"
}
let getters = {
  getName(state){
    return `我的名字是:${state.name}`;
  }
}
let mutations = {}
let actions = {}

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions
})

vue内的代码:

<template>
  <div>
    {{username}}
  </div>
</template>

<script>
  export default {
    data(){
      return{
        message: '我是一个菜鸟',
      }
    },
    computed:{
      username(){
        return this.$store.getters.getName;
      }
    }
  }
</script>

chrome页面的效果图:


getters总结: getters主要是用来过滤和重组。这些事件最好也是能在计算属性中完成,用于监听事件变化的。

2.3、mutations:

来看下面一段代码:
vue代码:

<template>
  <div>
    {{username}}
    <div>
      <button @click="change">更改我的昵称</button>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        message: '我是一个菜鸟',
      }
    },
    methods:{
      change(){
        this.$store.commit("changeName","jkb...")
      }
    },
    computed: {
      username() {
        return this.$store.getters.getName;
      }
    }
  }
</script>

store代码:

let mutations = {
  changeName(state,name){
    state.name = name;
  }
}

点击后效果:


mutations总结:在 Vuex store 中,实际改变 状态(state) 的唯一方式是通过 提交(commit) 一个 mutation。mutations下的函数接收state作为参数,两一个参数叫做payload(载荷),payload是用来记录开发者使用该函数的一些信息,比如说提交了什么,提交的东西是用来干什么的,包含多个字段,所以载荷一般是对象(其实这个东西跟git的commit很类似)还有一点需要注意:mutations方法必须是同步方法!

2.4、 actions:

来看下面一段代码:
vue的代码:

 change(){
        this.$store.dispatch("changeName","jkb...")
      }

store的代码:

let mutations = {
  changeName(state,name){
    state.name = name;
  }
}
let actions = {
  changeName({commit},name){
    let timer = setTimeout(()=>{
      commit("changeName",name);
      clearTimeout(timer);
    },1000)
  }
}

actions总结:actions的作用其实和mutations是没有差别的,无非就是一个同步、异步的差别罢了。而在功能上面主要有一下两个区别:

  • actions 提交的是 mutations,而不是直接变更状态。也就是说,actions会通过mutations,让mutations帮他提交数据的变更。
  • actions 可以包含任意异步操作。ajax、setTimeout、setInterval不在话下。

最简单的使用,其实也就这些 但是很可能会存在很多深沉次的思考,接下来 我就趁热打铁,写一写vuex深沉次的思考。

三、 vuex的深层次思考

关于深层次的思考,我也做了这么久了。我其实主要的考量有两个地方:

  • 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。
  • 当页面的属性过多的时候,代码的可维护性下降。

针对上面的情况,我从这俩个切入点 说说我的想法:

1、 页面计算属性重复和冗余问题:

关于页面计算属性重复和冗余的问题,其实官方存在一个map函数来解决这个问题。来看下面一段代码。

  import {mapState,mapGetters} from "vuex"

  computed: {
      ...mapState([
        "name",
        "type"
      ]),
      ...mapGetters([
        "getName"
      ])
    }

这种做法也能达到效果。当然也可以进行重新命名的,例如这样:

  ...mapState({
        name:state=>state.name,
        type:state=>state.type
      })

2、如何按功能模块分state属性:

当我们项目开始复杂起来的时候,我们发现单个的vuex文件已经无法去满足我们的眼球了,当然也是能够进行维护的,只是维护起来的成本开销比较大。此时就需要引入一个新的概念:Module. module主要的功能是将大型的store分成一个个模块。
来看下面一段代码:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    moduleA,
    moduleB
  }
})

这样来获取的时候:

 ...mapState({
        a: state => state.moduleA.a,
})

这里有一个问题,就是在派发(dispatch)的时候,是没有做任何的区别的,所以很可能会遇到两个模块的actions事件名字是一样的。我在这里呢?看到了 一个新的解决办法--命名空间。来看下面一段代码:

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

当然modules里面还有很多深层次的使用方法,这就需要你去做更深层次的研究了,例如在带命名空间的模块内访问全局内容、在带命名空间的模块注册全局 action...等等。在实际的开发中其实会用到很多的 这就需要去读者朋友更深层次的进行学习和使用了。

四、 vuex的封装

前面说了这么多,又到了愉快的封装时间了。
store/index.js:

import vuex from 'vuex'
import aModule from "./a"
...modules
import vue from 'vue'

vue.use(vuex)
export default new vuex.Store({
  modules: {
    aModule
    ...modules
  }
})

store/aModule/types.js //主要用来封装事件名

export default {
  ACTIONS_NAME: "ACTIONS_NAME"
}

store/aModule/actions.js //主要用来处理逻辑、注册事件

import types from "./types";
export default {
  actions ({ commit }, data) {
    commit(types.ACTIONS_NAME, data)
  }
}

store/aModule/mutations.js //主要用来修改/保存状态

import types from "./types"

export default {
  [types.ACTIONS_NAME](state, data) {
    // set state
  }
}

store/aModule/index.js

import actions from "./actions";
import mutations from "./mutations"
const state = {};
export default {
  namespaced: true,
  state:state,
  mutations:mutations,
  actions:actions,
  getters:{
  }
}

说在最后

其实vuex真的是一个很不错的插件,也比较契合我们的开发习惯。当然很多人还是习惯用上面那几种方式来维护代码,但是我真的建议所有的程序员都在看看这个插件,学起来很简单 用起来也特别简单 最近在开发小程序之余(公司需求,没有用mpvue,用原生开发),也去研究了一下vuex的源码,如果有所得,还是会分享给大家。

推荐阅读更多精彩内容