项目地址在 https://github.com/Hard-workingrookie/todoList_vuex

组件之间共享数据的方式

父向子传值: v-bind 属性绑定
子向父传值: v-on 属性绑定
兄弟组件之间共享数据 :EventBus

  • $on 接收数据的那个组件
  • $emit 发送数据的那个组件

Vuex 是什么

Vuex是实现正在组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享。

Vuex 的基本使用

安装 Vuex 依赖包

1
npm install vuex --save

导入 Vuex 包

1
2
import Vuex from 'vuex'
Vue.use(Vuex)

创建 store 对象

1
2
3
const store =new Vuex.Store({
state:{count:0}
})

将 store 对象挂载到 vue 实例中

1
2
3
4
5
6
new Vue({
el:'#app',
render:h=>h(app),
router,
store
})

使用 Mutation 变更 store 中的数据

1
2
3
4
5
6
7
8
9
10
11
12

mutations: {
add (state) {
state.count++
},
sub (state) {
if (state.count > 0) {
state.count--
}
}
}

在页面使用

1
2
3
addCount () {
this.$store.commit('add')
}
1
2
3
subCount () {
this.$store.commit('sub')
}

mutations 带参传送

1
2
3
 addN (state, step) {
state.count += step
}
1
2
3
4
addCount () {
this.$store.commit('addN', 3)
}

Mutation 触发

this.$store.commit() 是触发 mutation 第一种方式

第二种:

1
import { mapMutations } from 'vuex'
1
...mapMutations(['add'])

带参:

1
2
3
4
5
6
methods: {
...mapMutations(['add', 'addN']),
addCount () {
this.addN(3)
}
}

不要在 mutations 中执行异步操作。action 用于执行异步操作

Action

action 用于执行异步操作

触发 action

第一种方式

不带参

1
2
3
4
5
6
7
actions: {
addAsync (context) {
setTimeout(() => {
context.commit('add')
}, 1000)
}
},
1
this.$store.dispath('addAsync')

带参

1
2
3
4
5
6
7
8
actions: {
addAsync (context,step) {
setTimeout(() => {
context.commit('addN',step)
}, 1000)
}
},

1
this.$store.dispath('addAsync',5)

第二种方式

1.导入:

1
import { mapAction } from 'vuex'

2.映射到当前组件的 methods 函数

1
2
3
4
methods:{
...mapAction(['addAsync','addNAsync'])
}

Getter

Getter 用于对 store 中的数据进行加工处理形成新的数据。

1
2
3
4
5
6
7
8
9
10
const store=new Vuex.Store({
state:{
count:0
},
getters:{
showNum:state=>{
return '当前最新的数量是【'+state.count+'】'
}
}
})

使用 Getter 的第一种方式

this.$store.getters.名称

使用 Getter 的第二种方式

1
import { mapGetters } from 'vuex'
1
2
3
computed: {
...mapGetters(['showNum'])
}
1
<h1>{{showNum}}</h1>

Modules

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}

const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块的局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},

getters: {
doubleCount (state) {
return state.count * 2
}
}
}

对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

1
2
3
4
5
6
7
8
9
10
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}

对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

1
2
3
4
5
6
7
8
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}

在带命名空间的模块内访问全局内容

如果你希望使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。

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
modules: {
foo: {
namespaced: true,

getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},

actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'

dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}

在带命名空间的模块注册全局 action

若需要在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,

actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}

模块动态注册

在 store 创建之后,你可以使用 store.registerModule 方法注册模块:

1
2
3
4
5
6
7
8
9
10
11
12
import Vuex from 'vuex'

const store = new Vuex.Store({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule('myModule', {
// ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})

之后就可以通过 store.state.myModule store.state.nested.myModule 访问模块的状态。

模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。