1.什么情况下需要使用Vuex?
- 不适用:在小型项目中使用 Vuex 在写法上是比较繁琐冗余的,通常,一个简单的 store模式 就足够您所需了。
- 适用于:构建一个中大型单页应用,Vuex可以更好地在组件外部管理状态,更方便于兄弟、父子、和跨级组件之间的状态共享,方法公用。
(1)多个视图使用同一状态/方法
methods: {
getData (data) {
this.stage = data.stage
this.$refs.detailInfo.getData(data)
this.$refs.productInfo.getData(data)
this.$refs.paymentPlan.getData(data)
this.$refs.attachFile.getData()
this.$refs.examRecord.getData()
this.$refs.changeRecord.getData(data)
this.$refs.contactInfo.getData(data)
this.$refs.orderInfo.getData(data)
this.$refs.ticketInfo.getData()
this.$refs.paidInfo.getData()
}
}复制代码
上述代码中,由于很多个子组件都使用了父组件中的data, 或子组件的方法调用依赖于父组件的状态更新,这时候如果用普通的父子组件通信方式,写法上是非常的繁琐,并且对于兄弟组件间的状态传递也是很难受。
(2)不同视图需要改变同一状态
<template>
<div>
<Card class="mgB-15 tabs-style">
<Tabs type="card">
<TabPane label="详细信息">
<detailInfo ref="detailInfo"/>
</TabPane>
<TabPane :label="label.productInfo">
<productInfo ref="productInfo" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.paymentPlan">
<paymentPlan ref="paymentPlan" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.attachFile">
<attachFile ref="attachFile" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.examRecord">
<examRecord ref="examRecord" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.changeRecord">
<changeRecord ref="changeRecord" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.contactInfo">
<contactInfo ref="contactInfo" @setLabel="setLabel"/>
</TabPane>
<TabPane :label="label.orderInfo" :disabled="disabled">
<orderInfo ref="orderInfo" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.ticketInfo" :disabled="disabled">
<ticketInfo ref="ticketInfo" @setLabel="setLabel" />
</TabPane>
<TabPane :label="label.paidInfo" :disabled="disabled">
<paidInfo ref="paidInfo" @setLabel="setLabel" />
</TabPane>
</Tabs>
</Card>
</div>
</template>复制代码
上述代码:多个子组件要修改父组件的label值,采用子组件抛出事件来变更和同步父组件的状态,只是修改了一个状态都如此麻烦,当要修改的数据比较多的时候,通常会导致无法维护的代码。自己写完的代码都不想再去看了,这谁扛得住啊。。。
2.快速上手Vuex核心api
Vuex的五大核心:state、mutations、actions、getters、modules
- state(辅助方法:mapState)
vuex使用的是单一的状态树,我们的vue应用将仅仅包含一个 store 的实例。所以当我们将store挂载到vue的实例上以后,我们可以通过this.$store取到vuex里面的各个部分。我们可以将state看作是单页面中的data,只不过state中的数据是公共的、全局的,任何组件都能实时访问到。如下:
export default new Vuex.Store({
state: {
username: 'admin',
password: '123456',
tenant: 1
}
})复制代码
如果不使用mapState函数,在页面中访问state中的数据如下:瞧瞧,这得多麻烦呀
export default {
name: 'Home',
mounted () {
console.log(this.username, this.password, this.tenant) // admin 123456 1
},
computed: {
username () {
return this.$store.state.username
},
password () {
return this.$store.state.password
},
tenant () {
return this.$store.state.tenant
},
}复制代码
当使用mapState辅助函数时候,如下:看看,这就舒服多了啊
import { mapState } from 'vuex'
export default {
name: 'Home',
mounted () {
console.log(this.username, this.password, this.tenant) // admin 123456 1
},
computed: {
...mapState([
'username',
'password',
'tenant'
])
}
}复制代码
小结:mapState的实际作用就是当一个组件需要获取多个状态时候,如果将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数帮助我们生成计算属性,让你少按几次键。
- mutations(辅助方法:mapMutations)
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
addCount (state, num) {
state.count = num
}
}
})复制代码
如下:你可以在组件中使用 this.$store.commit('方法名', '传递的参数')
提交 mutations
import { mapState } from 'vuex'
export default {
name: 'Home',
mounted () {
this.$store.commit('addCount', 666)
console.log(this.count) //666
},
computed: {
...mapState([
'count'
])
}
}复制代码
mapMutations的写法和上文提到的mapState
的写法基本一致, 这里我们主要使用数组的形式来说明。一切,都是语法糖...
import { mapState, mapMutations } from 'vuex'
export default {
name: 'Home',
mounted () {
this.addCount(777)
console.log(this.count) //777
},
methods: {
...mapMutations([
'addCount'
])
},
computed: {
...mapState([
'count'
])
}
}复制代码
- actions(辅助方法:mapActions)
actions 类似于 mutations,不同在于:
(1)actions 的方法是通过dispatch方式触发,在方法内不能直接变更state, 只能通过commit提交mutations中的方法去修改。
(2)actions中 可以包含任意异步操作,通常用来写一些公共的异步请求方法
import { query } from '@/api/index' //这是获取用户信息的api方法
export default new Vuex.Store({
state: {
userInfo: {}
},
mutations: {
setUser (state, data) {
state.userInfo = data
}
},
actions: {
getUser (context) {
console.log(context, 'context信息')
return query().then(res => {
context.commit('setUser', res.data)
return res
})
}
}
})复制代码
如下:你可以在组件中使用 this.$store.dispatch('方法名')
提交 actions
import { mapState, mapMutations } from 'vuex'
export default {
name: 'Home',
mounted () {
this.$store.dispatch('getUser').then(res => {
console.log(this.userInfo, '获取用户登录信息')
})
},
methods: {
...mapMutations([
'setUser'
])
},
computed: {
...mapState([
'userInfo'
])
}
}复制代码
上述代码打印如下:
mapActions的写法和上文提到的mapState,mapMutations
的写法也基本一致, 这里我们主要使用数组的形式来说明。一切,也都是语法糖...
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
name: 'Home',
mounted () {
this.getUser().then(res => {
console.log(this.userInfo, '999')
})
},
methods: {
...mapMutations([
'setUser'
]),
...mapActions([
'getUser'
])
},
computed: {
...mapState([
'userInfo'
])
}
}复制代码
- getters(辅助方法:mapGetters)
辅助函数其实就是个语法糖,方便我们去使用store,鉴于上面啰嗦太多,现在可以直接使用mapGetters辅助方法了吧,翠花,上代码:
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
addCount (state, data) {
state.count = data
}
},
getters: {
newCount (state) {
return state.count * 2
}
}
})复制代码
上述代码:newCount的值是根据count的值计算而来的。在页面获取newCount值如下:
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'Home',
mounted () {
this.addCount(18)
console.log(this.newCount, '根据count计算而来') // 36 "根据count计算而来"
},
methods: {
...mapMutations([
'addCount'
])
},
computed: {
...mapState([
'userInfo'
]),
...mapGetters([
'newCount'
])
}
}复制代码
- modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
如下:
// 这是index.js文件
import user from '@/store/modules/user'
import contract from '@/store/modules/contract'
export default new Vuex.Store({
modules: {
user,
contract
}
})复制代码
既然分了模块,那么如果各个模块的方法名难免会有重名的情况,该怎么办呢?这里可以设置namespaced: true 使用命名空间来解决这个问题。具体如下:
// 这是user.js文件
export default {
namespaced: true,
state: {
count: 10
},
mutations: {
setCount (state, num) {
state.count = num
}
},
actions: {
getCount ({commit}) {
commit('setCount', 6)
}
},
getters: {
newCount (state) {
return state.count * 6
}
}
}
复制代码
//这是contract.js文件
export default {
namespaced: true,
state: {
count: 20
},
mutations: {
setCount (state, num) {
state.count = num
}
},
actions: {
getCount ({commit}) {
commit('setCount', 6)
}
},
getters: {
newCount (state) {
return state.count * 2
}
}
}复制代码
我们可以看到,两个store模块的文件方法名全部一样,开启命名空间后,在页面上使用如下:
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {
name: 'Home',
mounted () {
console.log(this.user, this.contract) // { count: 10} , { count: 20}
console.log(this.newCount,this.newCount1); // 40 60
},
methods: {
...mapMutations([
'addCount'
])
},
computed: {
...mapState([
'user',
'contract'
]),
...mapGetters({
newCount: 'user/newCount', // 使用命名空间,可以在页面重新定义名字,使得各模块的方法名即使一样也不会冲突
newCount1: 'contract/newCount',
})
}
}复制代码