vuejs全家桶之vuex
什么是vuex?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
总结一下上面官方给出的解释:
注意1: vuex 只能在vuejs项目使用
注意2:为 vuejs 项目提供统一的数据仓库(我们可以把vuejs项目中所有的模型数据都统一放置在 vuex,为了解决组件间通信的问题)
注意3:vuex 里面管理的数据是单向的数据流(在操作数据的时候,只能按照一个方向去操作,或者换句话说,我们不能直接的操作数据,需要遵循 vuex 规范才可以操作数据。
注意4:提供一个调试的工具,非常方便的查看vuex管理的数据。
vuex状态管理思想
为什么要使用vuex?
- 可以方便组件间的通信问题(重点)
- 可以提供缓存的特性(异步请求浏览器缓存)
- 可以很好的做调试(vuejs devTools)
同步修改数据和异步修改数据
vuex的使用
1, vuex 只能在vuejs项目使用,所以我们用vue-cli这个脚手架快速构建一个vue项目
yarn add @vue/cli
npx vue create myapp
2,下载vuex,并在项目的main.js文件中导入并使用
import Vuex from 'vuex'
Vue.use(Vuex)
3,(1)创建一个仓库(store)(2)提供state(3)提供修改数据的mutation
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
},
(4)注册store
new Vue({
//store在这里注册,然后渲染到页面上
store,
router,
render: h => h(App)
}).$mount('#app')
4,同步修改数据
(1)在App.vue主组件中的template中,先写一个按钮组,并定义点击事件,加减操作span里面的数据,这里是定义页面触发行为,然后创建一个生命周期函数,然后将函数导出
(2)在生命周期函数里面的methods中提交(commit)加减函数到mutation中
(3)在store的mutation中,定义加减函数应该进行什么操作(修改state),然后渲染页面
(4)将仓库里的state映射到组件的data里面(需要映射成计算属性才行)
<1> 在App.vue中引入mapState
import { mapState} from ‘vuex’;
<2>然后在computed里面映射
…mapState({countAias: ‘count’,movieData:‘movieData’})
<3>底层原理
countAias:function(){
return this.$store.state.count
}
(5)把仓库里面的mutations映射到组件中的methods
<1> 在App.vue中引入mapMutations
import { mapState,mapMutations} from ‘vuex’;
<2>然后在methods里面映射
…mapMutations([‘increment’,‘decrement’]),
<3>底层原理
increment:function () {
this.KaTeX parse error: Expected 'EOF', got '}' at position 31: …ncrement') }̲, decrement:fun…store.commit(‘decrement’)
}
5,异步修改数据
(1)在App.vue中定义页面触发行为
(2)页面触发行为,dispatch(分发)在store中的action,在action中完成异步操作,去向服务器请求数据
(3)action请求完成以后,将响应回来的数据commit到mutation中
(4)然后将请求回来的数据保存下来,所以这里向action操作完了之后,要commit action 和payload
(5)我们希望仓库帮我们管理数据,保存数据到仓库
(6)将在仓库里面的数据映射到data里
…mapState({countAias: ‘count’,movieData:‘movieData’})
(7)将action映射到methods里面
…mapActions({clickHandler:‘getMovie’}),
底层原理
clickHandler:function(){
this.$store.dispatch(‘getMovie’)
},
(8)App.vue的template中渲染
(9)筛选(getters专门负责对数据的筛选过滤操作)
<1>在store 中定义一个getter,这里定义是一个方法,按照属性方式使用,和计算属性一样
<2>定义一个方法,筛选信息,将信息返回到页面中
<3>将getters映射到computed中
import {mapState, mapMutations, mapActions, mapGetters} from ‘vuex’;
…mapGetters([‘returnStarBig12w’])
(10)vuex的缓存特性
<1>当第一次向服务器发送网络请求是,浏览器会对数据进行缓存,我们已经有了数据,但是在这里第二次第三次还是会向服务器请求数据,这样太耗费性能
<2> 我们可以在向服务器发送网络请求前,判断一下仓库里面有没有数据,如果没有,发送网络请求,如果有,则不发送
(11)loading效果
由于网络请求所耗费的时间是不确定,意味用户等待的时长也是不确定,为了防止乱点击,烦躁。一般在发送网络请求后,会在页面上出一个 loading的效果。数据成功回来后,loading消失。
1. loading.gif 2. 插件 3. css3 loading 效果
<1>我用css3loading效果,先在App.vue中放置html,和样式
<2>因为loading和数据是互斥的,所以我们分别给他们两个加上v-if=“flag”,v-else
<3>我们让flag映射到data里面
…mapState({countAias: ‘count’,movieData:‘movieData’,flag:‘flag’}),
<4>在action中,还没有网络请求的时候进行提交commit,payload默认为true,也就是loading是默认显示的
store.commit(‘modifyFlag’, true);
<5>在mutation中关于modifyFlag进行操作
<6>在已经请求回来数据时(savemovieData),将flag改为false
(12)调试
store:
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0,
//异步修改数据第二步:因为我们希望仓库来帮我们管理接口数据,所以我在这里放一个电影列表的容器,待会我把数据请求回来了就放在这里,这一步做完了之后,我们让页面触发一个行为,dispatch
movieData:[]
},
//mutation接收到提交过来的行为和载荷,操作仓库里的数据state
mutations: {
//store里面有一个state数据,这是一个对象,对象里面含有count数据
//当点击加号的时候,count=count+payload
increment (state) {
state.count += 1
},
//store里面有一个state数据,这是一个对象,对象里面含有count数据
//当点击加号的时候,count=count+payload
decrement (state) {
state.count -= 1
},
//异步修改数据第五步:保存数据到仓库,state 是数据
saveMovie(state,payload){
state.movieData=payload
state.flag=false
},
modifyFlag:function(state,payload){
state.flag=payload
}
},
//异步修改数据第四步:action向服务器端请求数据,然后将请求回来的数据保存下来,所以这里向action操作完了之后,要commit action 和payload
actions:{
getMovie:function(store, payload){
var url='https://movie.52kfw.cn/index.php/Api/Movie/alst?page=1&size=20'
//当第一次向服务器发送网络请求是,浏览器会对数据进行缓存,我们已经有了数据,但是在这里第二次第三次还是会向服务器请求数据,这样太耗费性能
// 我们可以在向服务器发送网络请求前,判断一下仓库里面有没有数据,如果没有,发送网络请求,如果有,则不发送
if(store.state.movieData.length>0){
console.log('数据来自缓存!')
return
}
//loading
store.commit('modifyFlag', true);
axios.get(url).then(response=>{
console.log(response)
if(response.status=== 200 && response.data.error_code===0){
//这里的保存数据的操作是对数据库进行操作,只有mutation才可以操作数据库,所以saveMovie这个方法要写在mutation当中
store.commit('saveMovie',response.data.result)
}
})
}
},
getters:{
returnStarBig12w:function(state){
console.log(state)
return state.movieData.filter(item =>{
return item.star>=120000;
})
}
}
})
export default store;
App.use:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<button @click="decrement">-</button>
<span>{{ countAias }}</span>
<button @click="increment">+</button>
<button @click="clickHandler">获取电影列表</button>
<div class="loading" v-if="flag">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<ul v-for="ele in movieData" v-else>
<li>{{ ele.id }}</li>
<li>{{ ele.actors}}</li>
<li>{{ ele.title}}</li>
</ul>
<h2>收藏数大于12w</h2>
<ul v-for="ele in returnStarBig12w">
<li>{{ ele.id }}</li>
<li>{{ ele.actors}}</li>
<li>{{ ele.title}}</li>
</ul>
<router-view/>
</div>
</template>
<script>
//可以将仓库里的state映射到组件的data里面(好处:不用写this.$store.state.count这么一大串了),但是需要映射成计算属性才行
//可以将仓库中的mutations(只能通过这个来修改仓库中的数据)映射到组件的method里面
import { mapState,mapMutations,mapActions,mapGetters} from 'vuex';
export default {
//数据初始阶段就开始了
created:function() {
console.log(this.$store);
},
computed:{
//将仓库里的state里的count和movieData映射到组件的data里面
...mapState({countAias: 'count',movieData:'movieData',flag:'flag'}),
...mapGetters(['returnStarBig12w'])
//上面这句话的底层原理就是下面这个函数,相当于返回了仓库里的count,好处:1,简洁,2防止重名可以用对象的方式重命名
// countAias:function(){
// return this.$store.state.count
// }
},
methods:{
//把仓库里面的mutations映射到组件中的methods中
...mapMutations(['increment','decrement']),
//上面这句话的底层原理如下
//点击网页上的按钮,页面触发一个改变数据的行为,直接使用commit向mutation提交action和payload
// increment:function () {
// this.$store.commit('increment',10)
// },
// decrement:function () {
// this.$store.commit('decrement',10)
// }
...mapActions({clickHandler:'getMovie'}),
//异步修改数据第三步:页面触发一个请求数据的行为,页面dispatch这个行为(action),然后action向服务器端请求数据
// clickHandler:function(){
// this.$store.dispatch('getMovie')
// },
}
}
</script>
<style lang="less">
ul{
li{
list-style: none;
}
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
.loading{
width: 80px;
height: 40px;
margin: 0 auto;
margin-top:100px;
}
.loading span{
display: inline-block;
width: 8px;
height: 100%;
border-radius: 4px;
background: lightgreen;
-webkit-animation: load 1s ease infinite;
}
@-webkit-keyframes load{
0%,100%{
height: 40px;
background: lightgreen;
}
50%{
height: 70px;
margin: -15px 0;
background: lightblue;
}
}
.loading span:nth-child(2){
-webkit-animation-delay:0.2s;
}
.loading span:nth-child(3){
-webkit-animation-delay:0.4s;
}
.loading span:nth-child(4){
-webkit-animation-delay:0.6s;
}
.loading span:nth-child(5){
-webkit-animation-delay:0.8s;
}
</style>
上一篇: Java函数式编程(五):闭包
下一篇: 在iframe中隐藏横向滚动条的方法大全