嗯,有了Markdown之后我更喜欢记一些学习笔记了,当作是一个记录吧。今天来写写Vuex,当然我是边看边写的,应该是会有一些乱而且可能会有不正确的地方,见谅。(而且感觉我都是大量Copy官方文档的啊)
Vuex是干嘛的?
用过React的同学都知道Redux,这是React的状态管理库,那么Vuex就是Vue的状态管理库。
什么是状态管理?在React中我们每个组件的数据都需要我们用 this.state.xxx
来设置,但是在Vue中就没有this.state而是data,让我们没有一个很直观的认识。我们可以大概地理解,state其实就是data中储存的数据,绑定到View上进行渲染。
但是,每个组件的data只能在本组件内访问,那当我们需要一些某几个不同的组件都能访问的state时好像就显得比较麻烦了(感觉用props也是比较麻烦的),这时我们也许就需要Vuex来管理这种数据了。(用官方文档的话来讲,避免破坏单向数据流的简洁性)
这里Copy一下官方的话,我们用Vuex把组件的共享状态抽取出来,以一个全局单例模式管理,这样就构成了一个state的tree
开始
这里有几个需要注意的地方:
- store在全局是唯一的
- 不能直接改变store中的state,必须显式地commit mutation,这样也是为了方便我们跟踪每一个state的变化
这里copy一下文档的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
console.log(store.state.count);
store.commit('increment');
|
这样我们就有了一个简单的计数器啦~~
State
官方文档中推荐使用 computed
来获取store中的状态,当然这里有一些骚操作,我直接来贴一下最后推荐的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const app = new Vue({ el: '#app', store, components: { Counter }, template: ` <div class="app"> <counter></counter> </div> ` })
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } }
|
另外,在模块化项目中,可以用 mapState
辅助函数来生成计算属性,用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { mapState } from 'vuex'
export default { computed: mapState({ count: state => state.count,
countAlias: 'count',
countPlusLocalState (state) { return state.count + this.localCount } }) }
|
Getter
与Computed的初衷相同的是,有时候我们会想让某个经常被访问的state的值被缓存起来,Vuex中允许我们定义getter来获取它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) }, doneTodosCount: (state, getters) => { return getters.doneTodos.length }, getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } } })
store.getters.doneTodos
|
另外,像上面的 mapState
一样,也提供了 mapGetter
方便映射
Mutation
在Vuex中,更改store中状态的唯一方法就是commit mutation(这里写在前面,mutation必须是同步事务,所以如果有两个回调函数调用它,是无法断言谁先调用的),写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { state.count++ } } })
store.commit('increment')
|
另外,我们可以通过payload来传入额外的参数。在大多数情况下,payload应该是一个对象,写法如下:
1 2 3 4 5 6 7 8 9 10 11
| mutations: { increment (state, payload) { state.count += payload.amount } }
store.commit('increment', { amount: 10 })
|
同样,Vuex提供了 mapMutations
来映射mutation
Action
mutation不支持异步操作,但是Vuex提供了新的异步操作方式:Action。Action通过提交mutation来实现修改状态,写法好像和mutation并没有什么区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
|
虽然写法没有什么区别,但是用法就有区别了。action通过store.dispatch方法来触发,并不能直接调用,这样做是为了能在Action中封装一些异步操作并且commit mutation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| store.dispatch('incrementAsync', { amount: 10 })
actions: { checkout ({ commit, state }, products) { const savedCartItems = [...state.cart.added] commit(types.CHECKOUT_REQUEST) shop.buyProducts( products, () => commit(types.CHECKOUT_SUCCESS), () => commit(types.CHECKOUT_FAILURE, savedCartItems) ) } }
|
我们可以看到,上面的示例中使用了异步操作来改变状态,异步函数在Action中被调用。
在Vuex中,作者也提供了一些Action的嵌套写法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } }
store.dispatch('actionA').then(() => { })
actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) }
actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') commit('gotOtherData', await getOtherData()) } }
|
可见,Action的支持异步特性让我们能够异步修改store里的状态
目前来说这些就已经够用了,下面贴一个在表单中绑定store中状态的方法:
第一种是利用v-model
来绑定本组件的state,再侦听input
或者change
事件来更新store中的状态:
1
| <input :value="message" @input="updateMessage">
|
1 2 3 4 5 6 7 8 9 10
| computed: { ...mapState({ message: state => state.obj.message }) }, methods: { updateMessage (e) { this.$store.commit('updateMessage', e.target.value) } }
|
另外一个是利用带有setter的双向绑定计算属性:
1
| <input v-model="message">
|
1 2 3 4 5 6 7 8 9 10
| computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } } }
|
嗯,Vuex初级学习大概就到这里了~~感觉还是不错的,这个全局状态管理还是挺有用的