一.Vuex是什么?为什么要使用它?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
大家可以思考一下,JavaScript开发的应用程序,已经变得越来越复杂了.JavaScript需要管理的状态越来越多,越来越复杂:
- 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等;
- 也包括一些UI的状态,比如某些元素是否被选中,是否显示加载动效,当前分页;
然而,当我们有多个组件共享一个共同的状态时,就没有这么简单了:单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态;
- 来自不同视图的行为需要变更同一状态;
因此vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进vuex中,任何组件都可以进行使用。
二.安装
使用npm或yarn安装
在这里插入# npm 安装
npm install vuex
# yarn 安装
yarn add vuex
三.配置
1.在src目录下创建一个store目录,在该目录下创建index.js文件,用于创建Store对象
//用于创建Vuex的核心对象Store
//1.导入
import {createStore } from 'vuex'
//2.创建Store对象
const store = createStore({
//state 用于存储数据
state(){
return {
count:1,
}
},
//actions 用于响应组件中的事件
actions:{
},
//mutations 用于操作数据
mutations:{
}
});
//3.暴露出store对象
export default store;
2.在main.js使用store对象
import store from './store';
app.use(store);
3.在Vue组件,可以通过this.$store
访问store实例
<template>
<h2>{{$store.state.count}}</h2>
</template>
<script>
export default {
name: 'App',
data() {
return {
};
},
components: {
},
methods: {
},
mounted() {
console.log(this.$store.state.count);
}
};
</script>
<style lang="css" scoped>
</style>
四.状态管理
4.1 State
提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data
在vuex中state中定义数据,可以在任何组件中进行调用
//1.导入createStore函数
import {createStore} from 'vuex'
//2.创建vuex的核心对象
//定义一个状态
const state={
count:0,
user:{id:1,username:'zhangsan',age:21},
username:'张三',
orders:[{
id:1,
pname:'华为P40',
price:3999,
num:1,
},{
id:2,
pname:'华为笔记本',
price:4999,
num:2,
}]
}
调用:
方法一:
在标签中直接使用
方法二:
this.$store.state.count(全局数据名称)
方法三:
从vuex中按需导入mapstate函数
import { mapState } from “vuex”;
...mapState({
count:state=>state.count*2,
username:state=>state.username,
4.2. Moutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:你可以向 store.commit
传入额外的参数,即 mutation 的载荷(payload):
你不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 INCREMENT
的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
increment:function(context,value){
context.commit("INCREMENT",{
value
});
},
INCREMENT(store,payload){
store.sum +=payload.value;
},
提交 mutation 的另一种方式是直接使用包含 type
属性的对象:
store.commit({
type: 'increment',
amount: 10
})
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此处理函数保持不变:
4.2.1 在组件中提交 Mutation
你可以在组件中使用 this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用(需要在根节点注入 store
)
<template>
<div class="box">
<h2>和:{{$store.state.sum}} --{{count}}</h2>
<input type="number" length="3" v-model="num"/>
<input type="button" value="+" @click="add({value:num})"/>
<input type="button" value="-" @click="sub({value:num})"/>
<Counter/>
</div>
</template>
<script>
import {mapMutations } from 'vuex'
export default {
name: 'CalcSum',
data() {
return {
num:1,
};
},
mounted() {
},
methods: {
/*
add(){
//通过dispatch去提交一个action
this.$store.dispatch('increment', this.num);
console.log(this.$store.state.sum)
//强制刷新页面
//this.$forceUpdate();
},
sub(){
this.$store.dispatch('decrement', this.num);
//this.$forceUpdate();
},*/
...mapMutations({
add:'INCREMENT',
sub:'DECREMENT'
}),
},
computed: {
count () {
return this.$store.state.sum
}
}
};
</script>
<style lang="css" scoped>
.box{
width:70%;
margin: auto;
}
</style>
如果组件的事件函数名与Mutation中修改状态的函数名一样,我们可以简写:
...mapMutations(['INCREMENT','DECREMENT']),
mutation重要原则:
一条重要的原则就是要记住 mutation 必须是同步函数,这是因为devtool工具会记录mutation的日记;每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照;但是在mutation中执行异步操作,就无法追踪到数据的变化;
所以Vuex的重要原则
;
4.3.Action
如果我们希望在Vuex中发送网络请求的话需要如何操作呢?那我们就使用Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
让我们来注册一个简单的 action:
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
4.3.1actions的分发操作
如何使用action呢?进行action的分发:
- 分发使用的是 store 上的dispatch函数;
- 同样的,它也可以携带我们的参数:
-
也可以以对象的形式进行分发:
乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:
4.3.2 actions的辅助函数
action也有对应的辅助函数:使用 mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(需要先在根节点注入 store
)
4.4 Getter
某些属性我们可能需要经过变化后来使用,这个时候可以使用getters:Vuex 允许我们在 store 中定义“getters”(可以认为是 store 的计算属性)
上图我们中函数Getter 接受 state 作为其第一个参数:
getters:{
totalPrice(state){
let total= 0;
state.books.forEach((book)=>{
total += book.count*book.price;
})
return total;
}
},
我们在其他组件可以访问Getter 对象,Getter 会暴露为 store.getters
对象,你可以以属性的形式访问这些值:
<div>
<h2>总价:{{$store.getters.totalPrice}}</h2>
</div>
getters可以接收第二个参数
getters:{
totalPrice(state,getters){
let total= 0;
state.books.forEach((book)=>{
total += book.count*book.price;
})
return total+","+getters.myName;
},
myName(state){
return state.name;
}
},
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
getters:{
totalPrice(state,getters){
let total= 0;
state.books.forEach((book)=>{
total += book.count*book.price;
})
return total+","+getters.myName;
},
myName(state){
return state.name;
},
getBookById(state){
return (id)=>{
return state.books.find(book=>
book.id === id
);
}
}
},
访问:
<h2>书籍信息:{{$store.getters.getBookById(3)}}</h2>
4.4.1 mapGetters 辅助函数
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
<template>
<div>
<h2>总价:{{totalPrice}}</h2>
<h2>书籍信息:{{getBookById(3)}}</h2>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'Book',
computed:{
...mapGetters(['totalPrice','getBookById']),
},
};
</script>
<style lang="scss" scoped>
</style>