11Vuex

11.1 状态管理与应用场景

11.1.1 状态管理模式

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

11.1.2 应用场景

在较大型的项目中,将有许多组件用到同一变量,比如,一个登录的状态,很多页面组件都需要这个信息。在这样的情景下,使用Vuex进行登录状态的统一管理就很方便。当然,虽然麻烦但也可以时刻在对应页面操作cookie。所以,状态管理不是必需的,所有状态管理能做的,都能用其它方式实现,但是状态管理提供了统一管理的地方,操作方便,也更加明确。但一些状态只是父组件和子组件共享,不推荐使用状态管理实现,而用$emitprops即可简单实现。

11.2 Vuex的安装与基本应用

1.安装Vuex

Vue Router一样,将Vuex添加到项目中也有4种主要方法:本地独立版本方法、CDN方法、NPM方法以及命令行工具(Vue CLI)方法。

2.项目文件中导入并显式地使用Vuex

使用Vue CLI安装Vuex后,首先,在项目的/src/store/index.js文件中,导入Vuex模块,并创建一个store(仓库)。

然后,在项目主文件main.js中导入Vuex,并显式地使用Vue实例调用Vuex

import { createApp } from 'vue'

import App from './App.vue'

import store from './store' //导入store目录中的index.jsVuex的创建与配置在该文件中

createApp(App).use(store).mount('#app')

11.3 Vuex的核心概念

Vuex应用的核心是store,即仓库。store实际上就是一个容器,它包含应用中大部分的状态(state),与单纯的全局对象不同,主要有两点区别。

1Vuex的状态存储是响应式的。也就是说,当Vue实例或组件从仓库store中读取状态时,若store中状态发生变化,那么相应的Vue实例或组件也会高效更新。

2)用户不能直接更新store中的状态。更新的唯一途径是显式地提交mutation(类似于事件),以便跟踪每一个状态的变化。

一个完整的store包含stategettersmutationsactionsmodules五大组成部分。

11.3.1 Vuex中的state

Vuex使用单一状态树,即使用一个对象包含了所有的应用层级状态,作为一个唯一的数据源而存在。也就是说,每个应用将仅包含一个仓库store实例。因此,需要状态跟踪(管理)的数据保存在Vuex选项的state选项内。

1.在 Vue 组件中通过computed计算属性获得 Vuex 状态

$storestore的区别

l  $store 是挂载在Vue 实例上的(即Vue.prototype),而组件也是一个Vue实例。在组件中可使用this访问原型上的属性,template拥有组件实例的上下文,可直接通过{{$store.state }}访问,等价于script 中的this.$store.state

l  store是挂载到Vue上,为Vue的根实例;store引入后被注入到Vue上,成为Vue的原型属性,所以store是挂载到Vue上,为Vue的根实例;store引入后被注入到Vue上,成为Vue的原型属性,所以在script中通过store.state$store.state都可以访问。

l  至于{{store.state}}script中的data需声明过store才可以访问。

2.在 Vue 组件中通过mapState()辅助函数获得 Vuex 状态

当一个组件需要获取多个状态时,将这些状态都声明为计算属性会有些重复和冗余。为解决这个问题,Vuex通过使用 mapState() 辅助函数帮助生成计算属性,减少按键次数。

mapState() 辅助函数返回的是一个对象,用来获取多个状态。mapState()可以接收{}[]作为参数。

{}参数为键值对形式参数,即key:valuekey为计算属性,value为函数,参数为store.state,返回需要的state

当映射的计算属性名称与state的子节点名称相同时,可以为mapState()传一个字符串数组参数。

computed: mapState([

  // 映射 this.count store.state.count

  'count'   //可以有多个state对象中属性,用逗号分隔

])

3.对象展开运算符

mapState()函数返回的是一个对象。如何将它与局部计算属性混合使用呢?通常,首先需要使用一个工具函数将多个对象合并为一个,然后将最终对象传给 computed 属性。但自从有了对象展开运算符(),可以极大地简化写法。

computed: {

  localComputed () { /* ... */ },

  // 使用对象展开运算符将此对象混入到外部对象中

  ...mapState({

    // ...

  })

}

11.3.2 Vuex中的getters

在工程项目中,有时需要store.state 中派生出一些状态,如对列表进行过滤并计数,可以通过计算属性来实现,具体代码如下。

computed: {

  doneTodosCount () {

    return this.$store.state.todos.filter(todo => todo.done).length

  }

}

Vuex允许在store中定义“getters(可以认为是 store 的计算属性)。getters可以接受state 作为其第一个参数,示例代码如下。

const store = createStore({

  state: {

    todos: [

      { id: 1, text: '...', done: true },

      { id: 2, text: '...', done: false }

    ]

  },

  getters: {

    doneTodos (state) {

      return state.todos.filter(todo => todo.done)

    }

  } })

1.通过属性访问getters

getters 会暴露为 store.getters 对象,可以以属性的形式访问这些值。示例代码如下。

store.getters.doneTodos // 返回[{ id: 1, text: '...', done: true }]

getters也可以接受其他 getters作为第二个参数,示例代码如下。

getters: {

  // ...

  doneTodosCount (state, getters) {

    return getters.doneTodos.length

  }

}

store.getters.doneTodosCount // 返回1

computed: {

  doneTodosCount () {

    return this.$store.getters.doneTodosCount

  }

}

2.通过方法访问getters

也可以让 getters 返回一个函数,来实现给 getters 传参。在对 store 里的数组进行查询时非常有用。示例代码如下。

getters: {

  // ...

  getTodoById: (state) => (id) => {

    return state.todos.find(todo => todo.id === id)

  }

}

store.getters.getTodoById(2) // 返回{ id: 2, text: '...', done: false }

3.通过mapGetters() 辅助函数访问getters

mapGetters() 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性,示例代码如下。

computed: {

  // 使用对象展开运算符将 getters 混入 computed 对象中

    ...mapGetters([

      'doneTodosCount',

      'anotherGetter',

      // ...

    ])

  }

...mapGetters({

  // `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`

  doneCount: 'doneTodosCount'

})

11.3.3 Vuex中的mutations

更改 Vuex store 中的状态的唯一方法是提交 mutations。每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

const store = createStore({

  state: {

    count: 1

  },

  mutations: {

    increment (state) {  // increment为事件类型typestate为参数

      // 变更状态

      state.count++

    }

  }

})

不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,需要以相应的 type 调用 store.commit()方法。store.commit('increment')

11.3.4 Vuex中的actions

actions 类似于 mutations,不同在于以下两点。

l  actions提交的是 mutations,而不是直接变更状态。

l  actions可以包含任意异步操作。

actions中的方法需要使用store.dispatch()方法调用。action函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit() 提交一个 mutation,或者通过 context.state context.getters 来获取 state getters

actions通过 store.dispatch() 方法触发mutations,如store.dispatch('increment')

可以在 actions内部执行异步操作,示例部分代码如下。

actions: {

  incrementAsync ({ commit }) {

    //每秒钟执行一次mutations中的increment

    setInterval(() => {

      commit('increment')

    }, 1000)

  }}

// 以载荷形式分发

store.dispatch('incrementAsync', {

  amount: 10

})

// 以对象形式分发

store.dispatch({

  type: 'incrementAsync',

  amount: 10

})

在组件中使用 this.$store.dispatch(‘xxx’) 方法分发 actions,或者使用 mapActions() 辅助函数将组件的 methods 映射为 store.dispatch()方法调用。

methods: {

    ...mapActions([

      'increment', //`this.increment()` 映射为`this.$store.dispatch('increment')`

      'incrementBy' //`this.incrementBy(amount)`映射为`this.$store.dispatch('incrementBy', amount)`

    ]),

    ...mapActions({

      add: 'increment' // `this.add()`映射为`this.$store.dispatch('increment')`

    })

  }

11.3.5 Vuex中的modules

应用的所有状态都集中到store对象中的缺点是:当应用变得非常复杂时,store可能变得相当臃肿。为解决该问题,Vuex允许将store分割成模块(modules)。每个模块拥有自己的statemutationactiongetter、甚至是嵌套子模块——从上至下进行同样方式的分割。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐