什么是 vuex
官网解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
划重点:
注意1: vuex 只能在vuejs项目使用
注意2:为 vuejs 项目提供统一的数据仓库(我们可以把vuejs项目中所有的模型数据都统一放置在 vuex,为了解决组件间通信的问题)
注意3:vuex 里面管理的数据是单向的数据流(在操作数据的时候,只能按照一个方向去操作,或者换句话说,我们不能直接的操作数据,需要遵循 vuex 规范才可以操作数据。(看图))。
注意4:提供一个调试的工具,非常方便的查看vuex管理的数据。
为什么要使用 vuex
- 可以方便组件间的通信问题(重点)
- 可以提供缓存的特性(异步请求)
- 可以很好的做调试
在使用 vux 之前,我们要知道他的数据是怎么修改的
数据存储修改方式
我们用下面这幅图来解释一下:我们提供了一个数据容器(仓库:store),数据(state)全部放在里面,组件要获取数据就从仓库里进行获取,并且如果组件要修改数据的话,要发送一个请求,在仓库里修改,自己不能修改数据
页面数据的改变
我们在上面就说过,不能再组件内修改数据,而是要把它传到仓库里,在仓库里进行修改,我们用下面这幅图来解释一下
1:如果是同步修改数据,就直接 commit ,将要进行的操作和数据都传给 Mutations, 在里面进行数据的修改
2:如果是异步数据修改,则要先发送一个异步请求 dispatch ,然后在 commit ,将要进行的操作和数据传给 mutations,进行数据的修改
vuex 的使用
1:安装 vuex
yarn add vuex
2:引入并且安装(在一个模块化的打包系统中,必须显式地通过 Vue.use() 来安装 Vuex)
import Vuex from 'vuex'
Vue.use(Vuex)//安装
3:使用 vuex
- 1:创建一个仓库
- 2:提供 state(数据)
- 3:提供修改参数的 mutations
- 4:把 store 注册到 vue 实例上,这样我们在所有的组件上都能通过 this.$store 来获取当前的仓库
const store = new Vuex.store({
// state:仓库里面的数据(之前说的模型数据),一般我们叫做 state 状态
state: {
//放数据
count: 0
},
mutations: {
// 这个对象里面定义对 state 的操作(curd)
}
})
new Vue({
//把 store 注册到 Vue 实例对象上面。以后在vuejs项目的所有的组件里面我们都可以通过 this.$store 获取到当前的仓库
store,
router,
render: h => h(App)
}).$mount('#app')
这样一个基础的仓库就完成了,以后我们就可以在里面进行数据的操作
我们来用一下这个仓库
在组件中获取数据
在上面创建一个仓库的时候,我们写了一个数据进去(你也可以写其他的或者不写),我们现在就到组件页面上去获取一下它(页面组件一般写在 views 文件夹下,我就在about 组件上改了,当然也可以自己在创建一个页面)
<template>
<div class="about">
<h1>This is an about page</h1>
<p>我是从仓库中获取的数据:{{ this.$store.state.count }}</p>
</div>
</template>
接下来我们来操作一下从仓库中获取的数据
操作数据
我们给一个加减按钮,使它可以改变
<template>
<div class="about">
<h1>This is an about page</h1>
<button @click="deCrease">-</button>
//template 里 this 可以不写
<p>我是从仓库中获取的数据:{{ this.$store.state.count }}</p>
<button @click="inCrease">+</button>
</div>
</template>
按照我们之前的做法,就直接在组件中修改
<script>
export default {
methods: {
deCrease: function(){
this.$store.state.count --
},
inCrease: function(){
this.$store.state.count ++
}
}
}
</script>
我们发现这样也能改变页面上的数据,主要是因为我们没有开启 vuex 的严格模式,如果开启了严格模式,是不允许直接修改仓库里面的 state ,并且 vuex 管理思想里面,说了数据的修改必须是通过 commit 进行提交,然后由 mutations 进行数据的修改。
接下来我们就开启一下严格模式(在 仓库的最上面加上:strict:true(默认为false)),然后由 mutations 来修改数据
1:在组件中把要触发的行为和数据传给 nutations
<script>
export default {
methods: {
deCrease: function(){
// this.$store.state.count --
//参数1:在 mutations 中要触发的行为
//参数二:传递的数据
this.$store.commit('deCrease',{ number: 1})//会触发 mutations 里对应的行为
},
inCrease: function(){
this.$store.commit('inCrease',{ number: 10})
}
}
}
</script>
2:在 nutations 里接收从组件传过来的行为和数据,并在这里进行处理
mutations: {
//deCrease:就是在单组件中设置的行为
//参数1:仓库里的 state
//参数2:单组件中 commit 时传过来的第二个参数(也就是从单组件中传过来的数据)
deCrease: function(state,payload){
state.count -- //每次减 1
},
inCrease: function(state,payload){
state.count += payload.number;//每次就加上我们传过来的数 10
}
}
接下来我们去操作一下异步数据
操作异步数据
1:在要获取数据(异步)的组件里
<script>
export default {
methods: {
getData: function(){
//自己不要做,告诉仓库,让仓库来做
//异步的话就要用 dispatch 来派发
this.$store.dispatch('getData');
}
}
}
</script>
2:异步操作数据写在 actions 里,同步操作写在 mutations 里
actions: {
//参数1:仓库对象
//参数2:组件中传过来的参数
getData: function(store,payload){
var url = 'https://movie.52kfw.cn/index.php/Api/Movie/alst?page=1&size=20'
axios.get(url).then((response)=>{
if(response.status === 200 && response.data.error_code === 0){
//把获取的数据 commit ,放到 mutations 里处理(这个时候已经获取到数据,在进行操作就是同步的了)
store.commit('saveData');
}
}).catch((error)=>{
console.log(error);
})
}
}
3:到 mutations 里处理异步获取回来的数据,把数据放到仓库里
//在 state 里定义一个属性来接收
saveData: function(state,payload){
state.getData = payload;
}
4:将数据在页面上显示,数据已经在仓库了,所以我们只要从仓库里拿数据就好了
<template>
<div>
<h1>我是列表页</h1>
<button @click="getData">获取数据</button>
<p>{{ $store.state.getData }}</p>
</div>
</template>
同步和异步数据怎么操作我们都知道了,现在我们来把代码优化一下
1:
<script>
// 需要从 vuex 里面导出一个函数 mapState(作用,可以把仓库里面的state映射到组件的 data 里面,要映射成计算属性才可以) map 映射 State 状态: 把 state 里面的状态隐射到组件的内容使用,减少 this.$store.state
import {mapState} from 'vuex';
export default {
computed: {
//如果 computed 里之前就存在其他属性,可以用展开运算符:...
//如果computed 本身就有一个getData,会产生冲突,我们可以给他取一个别名:...mapState({mydata: 'getData'})
...mapState(['getData'])
/*
* mapState(['getData']):底层
* count: function(){
return this.$store.state.count;
}
*/
}
}
</script>
这样我们就可以直接用 getData,而不需要写那么长
既然 state 可以映射,那么我们仓库里的其他属性应该也可以映射
仓库里面的 mutations 映射到组件的 methods 里面,则我们就可以直接修改 state
<script>
//仓库里面的 mutations 映射到组件的 methods 里面,则我们就可以直接修改 state,同步用mapMutations,异步用mapActions
import {mapState, mapMutations, mapActions} from 'vuex';
export default {
name: "list.vue",
methods: {
...mapActions(['getData'])
// getData: function(){
// //自己不要做,告诉仓库,让他来做
// //异步的话就要用 dispatch 来派发
// this.$store.dispatch('getData');
// }
},
computed: {
...mapState(['getData'])
/*
* mapState(['getData1']):底层
* count: function(){
return this.$store.state.count;
}
* */
}
}
</script>
仓库里除了 actions 和 mutations,还有其他操作,我们来看一下 getters
getters 是专门负责数据的过来操作
注意:它的特性是:定义是一个方法,按属性的方法使用
getters: {
idMore2: function(state){
return state.getData1.filter(item=>{
return item.id>2
})
}
}
缓存
网络请求,完全可以做个内存缓存,先去检测仓库里面是否存在数据,如果存在,则不发送网络请求;不存在发送 后台,肯定上缓存 memcache redis 内存缓存 io 》》》 磁盘IO mysql
//在我们要发送异步请求的地方
if( store.state.movieData.length > 0 ){
// 代表用户之前肯定点击获取电影,发送过网络请求
console.log('数据来自缓存信息!');
return;
}
由于网络请求所耗费的时间是不确定,意味用户等待的时长也是不确定,为了防止乱点击,烦躁。一般在发送网络请求后,会在页面上出一个 loading的效果。数据成功回来后,loading消失。
1 :在页面组件上
放置一个 loading 效果 loading 和 下面的列表展示是互斥的。 硬币的投掷就是一个互斥事件;一个事情存在正反两面,如果正发生,发面一定不会发生;如果发面发生了,正面一定不会发生
<div class="loading" v-if="flag">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<ul v-else v-for="ele in movieData">
<li>序号:{{ ele.id }}</li>
<li>名称:{{ ele.title }}</li>
<li>收藏数:{{ ele.star }}</li>
</ul>
2:在 state 里定义 flag,默认为false
3:修改状态
store.commit('modifyFlag', true);//在还没有数据的时候为 true
4:在 actions 里定义modifyFlage 的操作
5:当数据来了,将其改为true
单一 store
将 main.js 里关于store 的操作放到另一个文件夹内,并导出(export default store)
在需要的地方引入:import store from ‘./store’
常量表
将仓库中的 actions,mutations 等行为定义为常量
1:在当前文件夹下创建一个 constant.js 文件
export const INCREMENT = 'inCrement'
export const DECREMENT = 'deCrement'
2:在需要的地方导入
import {INCREMENT, DECREMENT} from './constant'
3:将对应的变量替换成常量
注:在 vuex 里尽可能吧 actions 和 mutations 里面的方法名称定义为常亮