TL;DR
- 单向数据流是state => view => action => state,而view是不能直接改变state的
- vuex就是数据管理,所有数据的修改都必须写成方法,放在mutations,只能通过
store.commit('xxmutation')
修改数据 - state/getters/mutations/actions,state注意没有s,代码里
mapState/mapGetters/mapMutations/mapActions
- actions常和请求使用,配合async和await
v-model
注意在computed那边设置getter setter
单向数据流是啥
<div>
<h1>{{title}}</h1>
<button @click="handleClick">将标题第一个字母大写</button>
<div>
- 大白话版:js的data里写了title => h1那边能显示出来,点击按钮 => js里method执行 => data的title的第一个字母大写了 => h1那边再次显示出来
- 专业名词版:state => view => action => state (和上面一一对应,感受下)
- 图片版:
其实数据不复杂的情况下,method就够用的。数据复杂之后,就有点云里雾里了。当然组件传递数据很麻烦的情况下,vuex也是方便很多。
vuex,其实就是管理数据的,所有数据的变化都必须通过方法add(1)
,不能直接xx.a=4
这种。然后就是专有名词和具体用法需要记忆了。
vuex的专业名词
vuex的专业名词:
- store,看代码
new Vuex.Store({state:{},mutations:{}})
,store是仓库,存着数据,存着改变数据的方法。 - state,就是数据,相当于
data
,state:{title:'hi'}
- getter,state派生出一些状态,相当于
computed
,但computed是通过this拿到data,而这里是通过参数访问store的state和getters,getters:{ doneTodosCount: (state,getters)=>{return getters.doneTodos.length} }
- mutation,类似于事件,相当于
method
,还是通过参数拿到state,参数直接放后面,mutations:{ add(state,n){ state.count + n } }
,调用的时候,store.commit('add',10)
,发现没,传方法名和参数就可以了,不用传state~ - action,上面的mutation只能同步,如果有异步,就需要用action了,但是改变数据,只能
commit(mutation)
,action的参数和上面都不一样,其是store实例,可以拿到store所以的东西,但一般commit居多,actions:({ add({commit}){commit('add')} })
,触发的方式store.dispatch('add')
,这样感受不到异步的特殊性,换种方式
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
action这边,其实特别重要,因为请求基本都是异步的,这边写下,结合请求使用action
state:{
listData:[]
},
mutations:{
setListData(state,data){
state.listData = data
}
},
// 另外的文件里,export function getList(){ return axios.get('/list').then(res => res.data)}
actions: {
async setListData ({ commit }) {
commit('setListData', await getList())
},
async setOtherListData ({ dispatch, commit }) {
// 如果有串联请求 先请求这个
await dispatch('setListData')
commit('setOtherListData', await getOtherList())
}
}
// 顺便写下 如果延时改变
actions: {
actionA ({ commit }) {
// 处理异步操作 当选promise
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
},
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
// 组件里 1s之后变值
store.dispatch('actionA').then(() => { ... })
// 或者
store.dispatch('actionB')
- module,所有的状态都在一个store里,可能渐渐就庞大了,和css类似,可以将store分割成模块,每个模块有自己的state、mutation、action、getter甚至是嵌套子模块。
const moduleA = { state: { ... }, mutations: { add(state,n){state.count+n} }, actions: { ... }, getters: { ... } }
const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } }
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
},
state:{}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
代码里怎么使用vuex
首先store是挂载在vue实例上的,所以所有的组件都能访问到store,new Vue({store})
。
组件里访问state
/* xx.vue v1.0.0 */
computed: {
count () {
// count就是在store那边
// Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中,这样子组件通过this.$store就可以访问
return this.$store.state.count
},
isLogin () {
return this.$store.state.isLogin
}
}
/* 但是当属性很多的时候,这样写太冗余了 用数组简化下 v2.0.0 */
let res = {}
['count', 'login'].map(prop => { res[prop] = () => this.$store.state[prop] })
// 用的时候
computed:{...res}
/* 索性可以定义成一个方法 v3.0.0 */
function mapState(propArr){
let res = {}
propArr.map(prop => { res[prop] = () => this.$store.state[prop] })
return res
}
// 用的时候
computed:{...mapState(['count', 'login'])}
/* 当然我们能想到的,vuex早就替我们想到了,所以vuex自己提供了mapState v4.0.0 */
// 当然mapState还可以对象形式,可以去官网看看
import { mapState } from 'vuex'
computed:{...mapState(['count', 'login'])}
组件里访问getters
用法同state,不赘述。
import { mapGetters } from 'vuex'
computed:{...mapGetters(['count', 'login'])}
组件里访问mutations
其实知道state的用法,这边就简单多了,两种形式,下面也写个例子:
// mutation怎么写的 回忆下 add(state,n){state.count+n}
/* 1. 直接组件里 this.$store.commit('add',1) */
/* 2. 组件里 把mutation放进methods */
methods:{
add (n) {
this.$store.commit('add', n)
}
}
// 多个的话
import { mapMutations } from 'vuex'
methods:{
...mapMutations(['add'])
}
组件里访问actions
用法同mutations,不赘述。
/* 1. 直接组件里 this.$store.dispatch('add',1) */
/* 2. 组件里 把actions放进methods */
import { mapActions } from 'vuex'
methods:{...mapActions(['add'])}
特别注意v-model
v-model会直接改变数据,违背了vuex
,所以不能像之前那样写,换个写法
// <input v-model="message">
// store.js
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
// xx.vue
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
加module的话
项目变大的时候,不可避免要用到模块区分
- 建store文件夹,里面再建modules文件夹,原先的store.js变成index.js放进来
- modules里面新建模块的数据
<!-- lotterySetting.js -->
export default {
// 注意 模块化管理数据请不要忘了命名空间的开启
namespaced: true,
state: {
},
mutations: {
}
}
- index.js里面,如下
import lotterySetting from './modules/lotterySetting'
export default new Vuex.Store({
modules: {
lotterySetting
},
- 组件使用的时候,state,mutation等等如下所示
computed:{
...mapGetters('lotterySetting',['List','number'])
},
methods:{
//方法一:
...mapActions('lotterySetting',['getListData','handleAdd']),
//方法二:
...mapActions({
getListData:"lotterySetting/getListData",
handleAdd:"lotterySetting/handleAdd"
})
vuex使用必须记住的
-
应用层级的状态应该集中到单个 store 对象中。
-
提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
-
异步逻辑都应该封装到 action 里面。