一.组件之间共享数据的方式
- 1.父传子:v-bind属性绑定,props接收
- 2.子传父:v-on事件绑定,on接收数据的那个组件,emit发送数据的那个组件
- 3.兄弟组件之间共享数据:EventBus,中央事件总线
二.Vuex是什么
- Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据的共享
三.使用Vuex统一管理状态的好处
- 1.能够在vuex中集中管理共享的数据,易于开发和维护
- 2.能够高效地实现组件之间的数据共享,提高开发效率
- 3.存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
四.什么样的数据适合存储在Vuex中
- 组件共享的数据
五.Vuex的基本使用
- 1.安装vuex依赖包
npm install vuex --save
- 2.导入vuex包(在store文件夹下的index.js页面)
import Vuex from 'vuex'
Vue.use(Vuex)
- 3.创建store对象(在store文件夹下的index.js页面)
const store = new Vuex.Store({
state:{num:''} // state中存放的就是全局的共享数据
mutations:{ } // mutations里面通常存放的是直接改变state里数据的事件
actions:{ } // actions里面通常存放的是异步请求的api等等,记住actions不可以直接更改state里面的数据,想修改需要通过context.commit触发mutations来修改
getters:{ } // 可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性
})
- 将store对象挂载到Vue实例中(main.js中)
new Vue({
router,
store, // 将创建的共享数据对象,挂载到Vue实例上,所有的组件就可以从store中获取全局的数据
render: h => h(App)
}).$mount('#app')
六.格式化配置文件(可以将分号去掉,双引号变为单引号)
// 在根目录中添加.prettierrc
{
"semi": false, // 去掉分号
"singleQuote": true // 双引号变为单引号
}
七.Vuex的核心概念
- 提供唯一公共数据源,所有共享的数据都要统一放到Store的State中存储
(一)State:
// 在store/index.js中定义全局的state下的数据,count
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count:0
}
})
export default store
- 组件访问State中数据的方式
// this.$store.state.全局数据的名称
// 在组件中使用(不加this是因为这是在html上)
<h3>加法:当前最新的值是:{{ $store.state.count }}</h3>
// 效果如下图
3. 组件访问State中数据的方式:
// 从vuex中按需引入mapState函数
import { mapState } from 'vuex'
// 通过导入的mapState函数,将当前的组件需要的全局数据,映射为当前组件的computed计算属性:
computed: {
...mapState(['count'])
}
// 在组件中使用
<h3>减法:当前最新的值是:{{count}}</h3>
// 效果如下图
(二)Mutations:(commit专门用来调用某个Mutations函数)
- 思考之路:
// 加法的情况下,当点击加号的时候,我们想让count+1,于是我们有了以下的思路
<template>
<div class="add">
<h3>加法:当前最新的值是:{{$store.state.count}}</h3>
<button @click="handleAdd">+</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
handleAdd () {
this.$store.state.count++
}
}
};
</script>
<style lang="scss" scoped>
.add {
display: flex;
margin: 20px;
button {
margin-left: 20px;
width: 25px;
}
}
</style>
// 效果如下:
//(注意注意注意:不可以直接操作Store中的数据!!!所以这种写法是错误的!!!)
2. Mutations用于变更Store中的数据
- (1)只能通过mutation变更Store数据state,不可以直接操作Store中的数据
- (2)通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监测所有数据的变化
- 触发mutations的方式(无参数传递)
// this.$store.commit('add') commit的作用,就是调用某个mutations函数
// 定义mutations
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
add(state) {
state.count++
}
}
})
<button @click="handleAdd"> +1 </button>
// 触发mutations
methods: {
handleAdd () {
this.$store.commit('add')
}
}
- 触发mutations的方式(有参数传递)
// this.$store.commit('addN')
// 定义mutations
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
addN (state,step) {
state.count += step // 变更状态,step可以由外界传递是几,就逐个加几
}
}
})
<button @click="handleAddN"> +N </button>
// 触发mutations
methods: {
handleAddN () {
this.$store.commit('addN', 3) // 每次点击加3
}
}
- 触发mutations的方式(无参数传递)
// 定义mutations
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
sub (state) {
state.count--
}
}
})
// 第一种触发mutations的方式
<button @click="handleSub">-1</button>
// 从Vuex中按需引入mapMutations函数
import { mapMutations } form 'vuex'
// 将mutations函数,映射为当前组件的methods方法
methods: {
...mapMutations(['sub']),
handleSub() {
this.sub() // 直接调用即可
}
}
// 第二种触发mutations的方式
<button @click="sub">-1</button> // 直接把sub函数写在这里
// 从Vuex中按需引入mapMutations函数
import { mapMutations } form 'vuex'
// 将mutations函数,映射为当前组件的methods方法
methods: {
...mapMutations(['sub'])
}
- 触发mutations的方式(有参数传递)
// 定义mutations
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
subN (state,step) {
state.count -= step
}
}
})
// 第一种触发mutations的方式
<button @click="handleSubN">-N</button>
// 从Vuex中按需引入mapMutations函数
import { mapMutations } form 'vuex'
// 将mutations函数,映射为当前组件的methods方法
methods: {
...mapMutations(['subN']),
handleSubN() {
this.subN(3) // 调用时直接传入参数即可
},
}
// 第二种触发mutations的方式
<button @click="subN(3)">-N</button> // 直接把subN写在这里,并传参
// 从Vuex中按需引入mapMutations函数
import { mapMutations } form 'vuex'
// 将mutations函数,映射为当前组件的methods方法
methods: {
...mapMutations(['subN']),
}
(三)Actions(dispatch专门用来调用某个Actions函数)
- 思考之路:在mutations中模拟异步操作
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
add (state) {
setTimeout(()=>{
state.count++
})
}
}
})
// 页面上显示的值会直接发生改变,但在vue的调试器vuetools,值是不变的
// 不要在mutations函数中,执行异步操作
- Action用来处理异步任务
- 如果通过异步操作变更数据,必须通过Actions,而不能使用Mutations,但是在Actions中还是要通过触发Mutations的方式间接变更数据
- 触发Actions的方式(无参数传递)
// 定义actions
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
state.count++
}
},
actions: {
addAsync(context) { // context相当于new出来的store的实例
setTimeout(() => { // 在actions中不能直接修改state中的数据,
context.commit('add') // 想修改必须通过context.commit()触发某个mutations
}, 1000);
}
})
<button @click="handleAddasync"> +1 Async </button>
// 触发actions
handleAddasync () {
this.$store.dispatch('addAsync')
}
- 触发Actions的方式(有参数传递)
// 定义actions
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addN(state, step) {
state.count += step
}
},
actions: {
addAsyncN(context, step) {
setTimeout(() => {
context.commit('addN', step) // 将step传递给commit
}, 1000);
}
}
})
<button @click="handleAddasyncN"> +N Async </button>
// 触发actions
handleAddasyncN () {
this.$store.dispatch('addAsyncN',5)
}
- 触发Actions的方式(无参数传递)
// 定义actions
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
sub(state) {
state.count--
}
},
actions: {
subAsync(context) {
setTimeout(() => {
context.commit('sub')
}, 1000);
},
}
})
// 第一种触发actions的方式
<button @click="handleSubasync"> -1 Async </button>
// 从Vuex中按需引入mapActions函数
import { mapActions } from 'vuex'
methods: {
...mapActions(['subAsync']),
handleSubasync () {
this.subAsync()
}
}
// 第二种触发actions的方式
<button @click="subAsync"> -1 Async </button>
// 从Vuex中按需引入mapActions函数
import { mapActions } from 'vuex'
methods: {
...mapActions(['subAsync']),
}
- 触发Actions的方式(有参数传递)
// 定义actions
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
subN(state, step) {
state.count -= step
}
},
actions: {
subAsyncN(context, step) {
setTimeout(() => {
context.commit('subN', step)
}, 1000);
}
}
})
// 第一种触发actions的方式
<button @click="handleAddasyncN"> -N Async </button>
// 从Vuex中按需引入mapActions函数
import { mapActions } from 'vuex'
methods: {
...mapActions(['subAsyncN']),
handleAddasyncN () {
this.subAsyncN(6) // 传入参数,每次减几
}
},
// 第二种触发actions的方式
<button @click="subAsyncN(6)"> -N Async </button> // 直接把subAsyncN写在这里,并传参
// 从Vuex中按需引入mapActions函数
import { mapActions } from 'vuex'
methods: {
...mapActions(['subAsyncN'])
},
(四)Getters:
- Getters的作用
- Getters可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性
- Store中的数据的变化, Getters的数据也会跟着变化(类似于响应式)
// 定义getters
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
showNumber (state) {
return '当前最新的数量 ['+ state.count + ']'
}
}
})
// 第一种触发getters的方式
<h3>{{$store.getters.showNumber}}</h3>
// 第二种触发getters的方式
<h3>{{ showNumber }}</h3>
// 引入mapGetters
import { mapGetters } from 'vuex'
computed: {
...mapGetters(['showNumber'])
},
(五)Mutations和Actions传入参数有两个或以上的时候
- mutations有两个或以上的参数的时候
// 定义mutations需要第二个参数传递一个payload,并解构得到里面的值
const store = new Vuex.Store({
state: {
count:0
},
mutations:{
addN (state,payload) {
const { index,text } = payload
state.count += step // 变更状态,step可以由外界传递是几,就逐个加几
}
}
})
// 触发mutations需要第二个参数传入一个payload对象,例子如下
methods: {
handleAddN (index,text) {
this.$store.commit('addN', {
index,
text
})
}
}
- actions有两个或以上的参数的时候
// actions一般用于获取接口api,在actions中获取到数据接口,然后通过context.commit赋值给Mutations的方法中,并在State的数据中定义好空数组用来接收api数据即可
// 定义actions需要第二个参数传递一个payload,并解构得到里面的值
const store = new Vuex.Store({
state: {
datalist:[]
},
mutations: {
setDate (state, datalist) {
state.datalist = datalist
}
},
actions: {
getDate(context, payload) {
const { key, testType} = payload
axios(
`/api&key=${key}&testType=${testType}` //假数据
).then((res)=> {
context.commit('setDate',res.data.result)
})
}
}
})
// 触发actions需要第二个参数传入一个payload对象
this.$store.dispatch('getDate',{ //传值对象
key:'123',
testType:'456'
})
(六)Modules
- 可以让每一个模块拥有自己的state、mutations、actions、getters,使得结构非常清晰,方便管理。
- 目录结构
// countOne/state.js
export default {
count: 0,
name: '尤大大'
}
// countOne/mutations.js
export default {
add(state) {
state.count++
}
}
// countOne/actions.js
export default {
addAsync(context) {
setTimeout(() => {
context.commit('add')
}, 1000);
}
}
// countOne/getters.js
export default {
showNumber(state) {
return '当前最帅的男人' + state.name + ''
}
}
// countOne/index.js
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
}
// countTwo类似于countOne,按照自己需求写入即可
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import counterOne from './counterOne'
import counterTwo from './counterTwo'
const store = new Vuex.Store({
modules:{
counterOne,
counterTwo
}
})
export default store
// counterOne的页面
<template>
<div class="add">
<h3>当前最新的值是:{{ count }}</h3>
<button @click="handleAdd">+</button>
<button @click="handleAddAsync">+Async</button>
<h3>Getters: {{showNumber}}</h3>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'
export default {
data() {
return {};
},
computed: {
...mapState('counterOne',{
count: state => state.count
}),
...mapGetters('counterOne',['showNumber'])
},
methods: {
...mapMutations('counterOne',['add']),
handleAdd () {
this.add()
},
...mapActions('counterOne',['addAsync']),
handleAddAsync () {
this.addAsync()
}
}
};
</script>
<style lang="scss" scoped>
.add {
display: flex;
align-items: center;
margin: 20px;
button {
height: 20px;
margin: 20px;
}
}
</style>
// counterTwo的页面
<template>
<div class="add">
<h3>当前最新的值是:{{ count }}</h3>
<button @click="handleAdd">+</button>
<button @click="handleAddAsync">+Async</button>
<h3>Getters: {{showNumber}}</h3>
</div>
</template>
<script>
import { mapGetters,mapActions,mapMutations, mapState } from 'vuex'
export default {
data() {
return {};
},
computed: {
...mapState('counterTwo',{
count: state => state.count
}),
...mapGetters('counterTwo',['showNumber'])
},
methods: {
...mapMutations('counterTwo',['add']),
handleAdd () {
this.add()
},
...mapActions('counterTwo',['addAsync']),
handleAddAsync () {
this.addAsync()
}
}
};
</script>
<style lang="scss" scoped>
.add {
display: flex;
align-items: center;
margin: 20px;
button {
height: 20px;
margin: 20px;
}
}
</style>
八.Vuex的选项卡demo
- 效果:点击选项一,选项一变红加粗,并出现相对应的内容一
2. 目录结构:
// views/tab.vue
<template>
<div>
<Tab></Tab>
<Content></Content>
</div>
</template>
<script>
import Tab from '@/components/tab/tab.vue'
import Content from '@/components/tab/content.vue'
export default {
components: { Tab,Content }
}
</script>
// components/tab/tab.vue
<template>
<div class="tab">
<p @click="selectIndex(0)" :class="[{change: selectIdx === 0}]" class="box">选项一</p>
<p @click="selectIndex(1)" :class="[{change: selectIdx === 1}]" class="box">选项二</p>
<p @click="selectIndex(2)" :class="[{change: selectIdx === 2}]" class="box">选项三</p>
<p @click="selectIndex(3)" :class="[{change: selectIdx === 3}]" class="box">选项四</p>
</div>
</template>
<script>
import {mapState,mapMutations} from 'vuex'
export default {
computed: {
...mapState('changeTab',{
selectIdx: state => state.selectIdx
})
},
methods: {
...mapMutations('changeTab',['selectIndex'])
}
}
</script>
<style lang="scss" scoped>
.tab {
display: flex;
.box {
margin: 20px;
}
.change{
margin: 20px;
color: red;
font-weight: 900;
}
}
</style>
// components/tab/content.vue
<template>
<div class="content">
<p>{{content[selectIdx]}}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
content:['页面一','页面二','页面三','页面四']
}
},
computed: {
...mapState('changeTab',{
selectIdx: state => state.selectIdx
})
}
}
</script>
<style lang="scss" scoped>
.content {
display: flex;
p {
margin: 20px;
}
}
</style>
// store/changeTab/state.js
export default {
selectIdx : 0
}
// store/changeTab/mutations.js
export default {
selectIndex (state,selectIdx) {
state.selectIdx = selectIdx
}
}
// store/changeTab/index.js
import state from './state'
import mutations from './mutations'
export default {
namespaced: true,
state,
mutations
}