大白话理解和初步使用vuex
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 里面。