Vue 入门到实战 十一
第11章 Vuex
11.1 状态管理与应用场景
11.1.1 状态管理模式
状态管理,管理的是全局状态,即全局变量。Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
11.1.2 应用场景
在较大型的项目中,将有许多组件用到同一变量,比如,一个登录的状态,很多页面组件都需要这个信息。在这样的情景下,使用Vuex进行登录状态的统一管理就很方便。当然,虽然麻烦但也可以时刻在对应页面操作cookie。所以,状态管理不是必需的,所有状态管理能做的,都能用其它方式实现,但是状态管理提供了统一管理的地方,操作方便,也更加明确。但一些状态只是父组件和子组件共享,不推荐使用状态管理实现,而用$emit和props即可简单实现。
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.js,Vuex的创建与配置在该文件中
createApp(App).use(store).mount('#app')
11.3 Vuex的核心概念
Vuex应用的核心是store,即仓库。store实际上就是一个容器,它包含应用中大部分的状态(state),与单纯的全局对象不同,主要有两点区别。
(1)Vuex的状态存储是响应式的。也就是说,当Vue实例或组件从仓库store中读取状态时,若store中状态发生变化,那么相应的Vue实例或组件也会高效更新。
(2)用户不能直接更新store中的状态。更新的唯一途径是显式地提交mutation(类似于事件),以便跟踪每一个状态的变化。
一个完整的store包含state、getters、mutations、actions、modules五大组成部分。
11.3.1 Vuex中的state
Vuex使用单一状态树,即使用一个对象包含了所有的应用层级状态,作为一个唯一的数据源而存在。也就是说,每个应用将仅包含一个仓库store实例。因此,需要状态跟踪(管理)的数据保存在Vuex选项的state选项内。

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

$store与store的区别
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:value,key为计算属性,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为事件类型type,state为参数
// 变更状态
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)。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
更多推荐


所有评论(0)