可有可无的状态管理模式?Vuex

287 阅读5分钟

什么是Vuex?

Vuex是专门用在vue的状态管理模式,Vuex通过把组件的状态(数据)统一管理在一棵状态树中,方便不同组件使用共同的数据源和修改数据。

img

Vue中的单向数据流主要是:

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

在没有Vuex的时候,组件之间共享状态只能通过传值,常见的方法如下:

1、父组件传子组件:使用v-bind或者简写:(绑定属性)

//parent.vue
<son :title="post.title"></son>

//son.vue
app.component('son', {
  props: {
    title: {
      type:String,
      default:''
    }
  }
})

2、子组件传父组件:使用v-on或者@(绑定事件)

//parent.vue
<son :value="value" @update:value="event" ></son>
//or parent.vue
<son v-model:value="value"></son>
//组件上的 v-model 使用 value 作为 prop 和 update:value 作为事件

//son.vue
app.component('parent', {
  setup(props,{emit}){
  	const change = ()=> {
      emit('update:value','123')
    }
  	return {
      change
    }
  }
})

3、兄弟组件互传:

兄弟组件可以以他们共同的父组件为媒介进行通讯,在简单应用中也是可取的,但是项目大了就难于理解和维护。

4、provide/inject:

Vue3中的新特性,中文翻译为:提供和注入,可以用于夸层级的通信,如下图中A和C组件之间的通信。

企业微信截图_ae5bdf33-118c-4b6e-9ef3-063efc049110.png

//parent.vue 组件A
app.component('parent', {
  provide: {
    user: 'John Doe'
  }
})

//children.vue 组件C 
app.component('children', {
  inject: ['user'],
})

在复杂的系统中,组件间的通讯相当频繁,单纯通过上面介绍的几种方法可以实现,但是逻辑将会很混乱也不利于维护。Vuex的出现就是为了解决这个问题。多个组件公用一个状态和多个组件改变同一个状态将变得很好维护。

vuex

Vuex的五脏六腑

Vuex把所有的状态都储存在store中,只有通过mutation来改变store的状态。如上图Vuex包含五个重要的概念:

1、State:

State是响应式的,所以在视图组件中可以使用computed来重新触发视图的变化,当然也是可以用watch

//监听或者计算以下的count状态
store.state.count

Vuex还提供了mapState辅助函数,让state状态变为视图中的计算属性变得更容易。

import { mapState } from 'vuex';
export default {
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

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

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

2、Getter:

Getter接受state作为第一个参数,其他的getter作为第二个参数。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

getters: {
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

3、Mutation:

提交mutation是更改state的唯一方法,和大部分的状态管理框架一样,Vuex也是为了方便追踪状态更改的来源而做的限制。

store.commit('increment')

mutation必须是同步函数,而且mutation也可以提交负载payload。

store.commit('increment', 10)

4、Action:

action通过提交mutation来改变state,这是为了统一记录谁促发了state的改变。

actions: {
    increment (context) {
      context.commit('increment')
    }
}

在视图组件中则用dispatch来分发action。

store.dispatch('increment')

action最重要的作用就是它可以是异步的方法,前面的mutation要求是同步的方法,所以在遇到异步的时候就需要使用action执行完异步任务后,再提交mutation来改变state。

在视图组件中调用异步的action,可以让action返回promise,之后dispatch该action副作用就可以了。

store.dispatch('actionA').then(() => {
  // ...
})

5、Module:

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

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

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

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

对于大型项目来说,简单的单一状态树确实有时候用起来捉襟见肘,所以vuex引入了模块化思想,允许把store分成不同的模块module。不同的mudole中的mutation和action都是注册在全局的,因此不同模块都可以对同一个action和mutation作出响应,所以vuex引入了命名空间。

namespaced: true,

只要在module中添加namespaced就可以区分开不同模块中的action和mutation,而不通模块之间的相互调用可以通过额外的参数来实现。比如getters中可以使用rootState, rootGetters来访问其他module中的方法和状态等。

getters: {
      // 在这个模块的 getter 中,`getters` 被局部化了
      // 你可以使用 getter 的第四个参数来调用 `rootGetters`
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      }
    },

协作分工

vuex

再来看看这张图,简单总结一下Vuex的工作流程:

1、Vue Components通过dispatch来分发actions,当然也可以直接提交mutation;

2、actions可以处理异步方法,比如请求接口数据等等,然后提交mutation;

3、mutation改变state中的状态;

4、state中的状态都是响应性的,直接促发视图Vue components的更新,完成一个循环;

5、可见,所有的状态改变都经过mutation,所以所有的状态改变都可以被记录,便于devtools的调试。

你以为结束了吗?

我还想介绍一个挺好用的插件:vuex-persistedstate

Vuex储存的状态只要浏览器一刷新就没了,对于需要持久化的数据来说有点不友好,比如登陆状态等等。

vuex-persistedstate使用浏览器的本地缓存(localStorage)对state进行储存,意味着如果你不主动删除或者更新,它将一直存在你的浏览器中。当然也可以把储存的方式改为sessionStorage后者cookies,只要配置一下就好了。

结合Vuex的模块化使用起来也很方便:

import Vuex from 'vuex';
import columns from './modules/example.js';
import createPersistedState from 'vuex-persistedstate';

export default Vuex.createStore({
  strict: true,
  state: {},
  getters: {},
  actions: {},
  mutations: {},
  modules: {
    columns,
  },
  plugins: [
    createPersistedState({
      key: 'setting',     //储存状态的key,也可以不配置
      paths: ['example'],   //example.js和vuex中普通的模块写法都一样
    }),
  ],
});

参考:

vuex.vuejs.org/zh/

mp.weixin.qq.com/s/oF-MJ39zh…

www.npmjs.com/package/vue…

微信公众号,更好的交流!

扫码_搜索联合传播样式-白色版.png