VueSupercharge 精通指南:构建超级状态管理 Vue.js 应用

发布时间:2024年01月18日

一、介绍

1.1?Vuex?是什么??

Vuex?是一个专为?Vue.js?应用程序开发的状态管理模式?+?库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

1.2?什么是“状态管理模式”?

这个状态自管理应用包含以下几个部分:

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

以下是一个表示“单向数据流”理念的简单示意:

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。

  • 来自不同视图的行为需要变更同一状态。

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

Vuex就是把组件的共享状态抽取出来,以一个全局单例模式管理?。
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

1.3?安装

npm install vuex@next --save

1.4?简单的Vuex使用

1.4.1?store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {},
  actions: {},
})

1.4.2?main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from "./store";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  store
})

1.4.3?App.vue

<template>
  <div>
    <my-addition></my-addition>
    <p>----------------------------------</p>
    <my-subtraction></my-subtraction>
  </div>
</template>

<script>
import Addition from "./components/Addition";
import Subtraction from "./components/Subtraction";

export default {
  data() {
    return {};
  },
  components: {
    'my-addition': Addition,
    'my-subtraction': Subtraction
  }
}
</script>

<style>

</style>

1.4.4?Addition.vue

<template>
  <div>
    <!-- 组件访问State中数据的第一种方式   -->
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button>+1</button>
  </div>
</template>

<script>
export default {
  name: "Addition",
  data() {
    return {};
  }
}
</script>

<style scoped>

</style>

1.4.5?Subtraction.vue

<template>
  <div>
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button>-1</button>
  </div>
</template>

<script>
export default {
  name: "Subtraction",
  data() {
    return {};
  }
}
</script>

<style scoped>

</style>

二、State

2.1?单一状态树

  • Vuex?使用单一状态树,用一个对象就包含了全部的应用层级状态。

  • 它便作为一个“唯一数据源?(SSOT)”而存在。这也意味着,每个应用将仅仅包含一个?store?实例。

2.2?方式一:通过$store获取State中数据

$store.state.count

2.3?方式二:通过mapState获取State中数据(推荐)

// 组件访问State中数据的第二种方式--引入
import {mapState} from 'vuex'

// 将全局数据,映射为当前组件的计算属性
...mapState(['count'])

// 模版中使用
<template>
  <div>
    <h3>当前最新的count值为:{{count}}</h3>
    <button>-1</button>
  </div>
</template>

三、Mutation

Vuex?中的?mutation?非常类似于事件:每个?mutation?都有一个字符串的事件类型?(type)和一个回调函数?(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受?state?作为第一个参数

3.1?方式一:使用commit触发mutation

使用commit触发mutation

// store.js
export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      state.count++;
    }
  },
  actions: {},
})

// Adition.vue
<template>
  <div>
    <!-- 组件访问State中数据的第一种方式   -->
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button @click="addHandler">+1</button>
  </div>
</template>

<script>
export default {
  name: "Addition",
  data() {
    return {};
  },
  methods: {
    addHandler() {
      // 这种方式是不合法的!!!
      // this.$store.state.count++;

      // 触发mutation的第一种方式
      this.$store.commit('add')
    }
  }
}
</script>

<style scoped>

</style>

带参数的mutation

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      state.count++;
    },
    // 第二位就是,传入的参数。如果是多个参数可以传入对象
    addN(state, step) {
      state.count += step;
    },
    sub(state) {
      state.count--;
    },
  },
  actions: {},
})
// Addition.vue
<template>
  <div>
    <!-- 组件访问State中数据的第一种方式   -->
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button @click="addHandler">+1</button>
    <button @click="addNHandler">+N</button>
  </div>
</template>

<script>
export default {
  name: "Addition",
  data() {
    return {};
  },
  methods: {
    addHandler() {
      // 这种方式是不合法的!!!
      // this.$store.state.count++;

      // 触发mutation的第一种方式
      this.$store.commit('add')
    },
    addNHandler() {
      this.$store.commit('addN', 3)
    }
  }
}
</script>

<style scoped>

</style>

3.2?方式二:使用mapMutation触发mutation(推荐)

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      state.count++;
    },
    addN(state, step) {
      state.count += step;
    },
    sub(state) {
      state.count--;
    },
    subN(state, step) {
      state.count -= step;
    },
  },
  actions: {},
})

Subtraction.vue

<template>
  <div>
    <h3>当前最新的count值为:{{count}}</h3>
    <button @click="subHandler">-1</button>
    <button @click="subNHandler">-N</button>
  </div>
</template>

<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations} from 'vuex'

export default {
  name: "Subtraction",
  data() {
    return {};
  },
  computed: {
    // 将全局数据,映射为当前组件的计算属性
    ...mapState(['count'])
  },
  methods: {
    ...mapMutations(['sub', 'subN']),
    // 将全局mutations函数映射为组件中的函数
    subHandler() {
      this.sub();
    },
    subNHandler() {
      this.subN(3)
    }
  }
}
</script>

<style scoped>

</style>


四、Action

Action的作用是执行异步函数,因为mutation中无法执行异步函数。在mutation中使用异步函数会导致state的状态无法被及时追踪导致代码异常!!!

4.1?方式一:使用dispatch调用Action中的异步函数

4.1.1?store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      // setTimeout(() => {
      //   // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
      //   state.count++;
      // }, 1000)
      state.count++;
    },
    addN(state, step) {
      state.count += step;
    },
    sub(state) {
      state.count--;
    },
    subN(state, step) {
      state.count -= step;
    },
  },
  actions: {
    // context: 可以理解为当前的Vuex.Store实例对象
    addAsync(context) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('add')
      }, 1000)
    },
    addNAsync(context, step) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('addN', step)
      }, 1000)
    }
  },
})

4.1.2?Addition.vue

<template>
  <div>
    <!-- 组件访问State中数据的第一种方式   -->
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button @click="addHandler">+1</button>
    <button @click="addNHandler">+N</button>
    <button @click="addHandlerAsync">+1 Async</button>
    <button @click="addNHandlerAsync">+N Async</button>
  </div>
</template>

<script>
export default {
  name: "Addition",
  data() {
    return {};
  },
  methods: {
    addHandler() {
      // 这种方式是不合法的!!!
      // this.$store.state.count++;

      // 触发mutation的第一种方式
      this.$store.commit('add')
    },
    addNHandler() {
      this.$store.commit('addN', 3)
    },
    addHandlerAsync() {
      // 这里的 dispatch 专门用来触发action函数
      this.$store.dispatch('addAsync')
    },
    addNHandlerAsync() {
      // 这里的 dispatch 专门用来触发action函数
      this.$store.dispatch('addNAsync', 3)
    }
  }
}
</script>

<style scoped>

</style>

4.2?方式二:使用mapAction调用Action中的异步函数

4.2.1?store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      // setTimeout(() => {
      //   // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
      //   state.count++;
      // }, 1000)
      state.count++;
    },
    addN(state, step) {
      state.count += step;
    },
    sub(state) {
      state.count--;
    },
    subN(state, step) {
      state.count -= step;
    },
  },
  actions: {
    // context: 可以理解为当前的Vuex.Store实例对象
    addAsync(context) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('add')
      }, 1000)
    },
    addNAsync(context, step) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('addN', step)
      }, 1000)
    },
    subAsync(context) {
      setTimeout(() => {
        context.commit('sub')
      }, 1000)
    },
    subNAsync(context, step) {
      setTimeout(() => {
        context.commit('subN', step)
      }, 1000)
    }
  },
})

4.2.2??Subtraction.vue

<template>
  <div>
    <h3>当前最新的count值为:{{count}}</h3>
    <button @click="subHandler">-1</button>
    <button @click="subNHandler">-N</button>
    <button @click="subHandlerAsync">-1 Async</button>
    <button @click="subNHandlerAsync">-N Async</button>
  </div>
</template>

<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions} from 'vuex'

export default {
  name: "Subtraction",
  data() {
    return {};
  },
  computed: {
    // 将全局数据,映射为当前组件的计算属性
    ...mapState(['count'])
  },
  methods: {
    ...mapMutations(['sub', 'subN']),
    ...mapActions(['subAsync', 'subNAsync']),
    // 将全局mutations函数映射为组件中的函数
    subHandler() {
      this.sub();
    },
    subNHandler() {
      this.subN(3)
    },
    subHandlerAsync() {
      this.subAsync();
    },
    subNHandlerAsync() {
      this.subNAsync(3)
    }
  }
}
</script>

<style scoped>

</style>

五、Getters

Vuex?允许我们在?store?中定义“getter”(可以认为是?store?的计算属性)。就像计算属性一样,getter?的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

5.1?创建Getter方法

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      // setTimeout(() => {
      //   // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
      //   state.count++;
      // }, 1000)
      state.count++;
    },
    addN(state, step) {
      state.count += step;
    },
    sub(state) {
      state.count--;
    },
    subN(state, step) {
      state.count -= step;
    },
  },
  actions: {
    // context: 可以理解为当前的Vuex.Store实例对象
    addAsync(context) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('add')
      }, 1000)
    },
    addNAsync(context, step) {
      setTimeout(() => {
        // 在 actions 中,不能直接修改 state 中的数据
        // 必须通过 context.commit() 触发某个 mutations 才行
        context.commit('addN', step)
      }, 1000)
    },
    subAsync(context) {
      setTimeout(() => {
        context.commit('sub')
      }, 1000)
    },
    subNAsync(context, step) {
      setTimeout(() => {
        context.commit('subN', step)
      }, 1000)
    }
  },
  getters: {
    showNum(state) {
      return '当前最新的数量是【' + state.count + '】';
    }
  }
})

5.2?方法一:$store.getter调用

<template>
  <div>
    <!-- 组件访问State中数据的第一种方式   -->
    <h3>{{$store.getters.showNum}}</h3>
    <button @click="addHandler">+1</button>
    <button @click="addNHandler">+N</button>
    <button @click="addHandlerAsync">+1 Async</button>
    <button @click="addNHandlerAsync">+N Async</button>
  </div>
</template>

<script>
export default {
  name: "Addition",
  data() {
    return {};
  },
  methods: {
    addHandler() {
      // 这种方式是不合法的!!!
      // this.$store.state.count++;

      // 触发mutation的第一种方式
      this.$store.commit('add')
    },
    addNHandler() {
      this.$store.commit('addN', 3)
    },
    addHandlerAsync() {
      // 这里的 dispatch 专门用来触发action函数
      this.$store.dispatch('addAsync')
    },
    addNHandlerAsync() {
      // 这里的 dispatch 专门用来触发action函数
      this.$store.dispatch('addNAsync', 3)
    }
  }
}
</script>

<style scoped>

</style>

5.3?方法二:mapGetters方式调用

<template>
  <div>
    <h3>{{showNum}}</h3>
    <button @click="subHandler">-1</button>
    <button @click="subNHandler">-N</button>
    <button @click="subHandlerAsync">-1 Async</button>
    <button @click="subNHandlerAsync">-N Async</button>
  </div>
</template>

<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex'

export default {
  name: "Subtraction",
  data() {
    return {};
  },
  computed: {
    // 将全局数据,映射为当前组件的计算属性
    ...mapState(['count']),
    // 将全局的getters,映射为当前组件的计算属性
    ...mapGetters(['showNum'])
  },
  methods: {
    ...mapMutations(['sub', 'subN']),
    ...mapActions(['subAsync', 'subNAsync']),
    // 将全局mutations函数映射为组件中的函数
    subHandler() {
      this.sub();
    },
    subNHandler() {
      this.subN(3)
    },
    subHandlerAsync() {
      this.subAsync();
    },
    subNHandlerAsync() {
      this.subNAsync(3)
    }
  }
}
</script>

<style scoped>

</style>

六、其他

6.1?modules

Module | Vuex

Vuex?允许我们将?store?分割成模块(module)。每个模块拥有自己的?state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

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 的状态

文章来源:https://blog.csdn.net/weixin_42559561/article/details/135677426
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。