一.状态管理
假设当一个数据变化时,多个组件同时更新视图. => 状态管理 => 应该把数据放到多个组件的共同祖先组件上
什么叫状态? => 状态就是响应式数据和呈现它的视图.
组件的状态是由它的数据和视图决定的.
状态管理 => 数据和视图管理.
状态管理 => 如何实现多个组件共用一个状态?
- 把数据和修改数据的逻辑放在父组件上.子组件通过props获取父组件数据,再通过自定义事件触发父组件修改数据的逻辑.(父传子,子传父)
- Vuex.(Vue的插件 => Vue的全局状态管理)
二.Vuex
Vuex如何实现:
- 如何实例化Vuex数据仓库.(用于存放数据和修改数据的逻辑)
- 组件如何获取Vuex的数据
- 组件如何修改Vuex的数据
- 如何把Vuex的数据和逻辑映射成组件的数据和逻辑
1. 如何实例化Vuex数据仓库.(用于存放数据和修改数据的逻辑)
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
// 实例化一个数据仓库
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 0
}
})
new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
// 挂载store
store
});
2. 组件如何获取Vuex的数据
挂载完成后,就可以通过Vuex的实例store来访问state内的数据。
vue组件内可以通过this.$store来获取这个state的实例。
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button>box--count++</button>
</div>
`,
computed: {
count() {
// 获取state的实例
return this.$store.state.count
}
},
}
3. 组件如何修改Vuex的数据
- 修改state数据的逻辑,需要通过mutations选项写在数据仓库中,可以写多个方法.
mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数.
组件触发mutations内的方法,需要同this.$store.commit来触发.
commit('mutation的方法名', 组件实参);
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 10000
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num;
},
// count-1
reduce(state, num) {
state.count -= num
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$store.commit("plus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
- 如果有异步操作的需求,需要通过actions来实现
actions方法内触发mutations方法.
actions的触发方式是dispatch.
dispatch('actions的方法名', 组件实参);
const store = new Vuex.Store({
// 严格模式.禁止在mutations之外修改state.
// 如果在mutations之外修改state,会报这个错[vuex] do not mutate vuex store state outside mutation handlers.
strict: true,
// 多个组件共用的状态.(数据)
state: {
count: 10000
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num
},
// count-1
reduce(state, num) {
state.count -= num
}
},
// 异步触发mutations
actions: {
asyncPlus(store, num) {
// 2秒后触发mutations的plus方法修改state。
setTimeout(() => {
store.commit("plus", num);
}, 2000);
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
4. 如何把Vuex的数据和逻辑映射成组件的数据和逻辑
map映射:
- 把state的数据变成组件数据.
- 把mutations和actions的方法变成组件方法.
const { mapState, mapMutations, mapActions } = Vuex;
// 以上这些方法,都是返回纯对象的.
// mapState(['count', 'msg']) => { count() {} , msg() {}}
const box = {
template: `
<div>
<h3>box组件---{{num}}---{{str}}</h3>
<button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
// count() {
// return this.$store.state.count
// }
// msg() {
// return this.$store.state.msg
// }
// ...mapState(['count', 'msg'])
...mapState({
num: 'count',
str: 'msg'
})
},
methods: {
...mapActions(['asyncPlus']),
...mapMutations(['reduce'])
}
}
5.Vuex的计算属性getters
<script>
const { mapState, mapMutations, mapGetters } = Vuex;
const store = new Vuex.Store({
state: {
count: 1,
price: 10,
},
// Vuex的计算属性total
getters: {
total({count, price}) {
return count * price
}
},
mutations: {
setCount(state, val) {
state.count = val
},
setPrice(state, val) {
state.price = val
}
}
});
const mx = {
computed: {
...mapState(['count', 'price'])
},
methods: {
...mapMutations(['setCount', 'setPrice'])
}
}
const count = {
template: `<input type='text' placeholder='数量' :value='count' @input='setCount($event.target.value)' />`,
mixins: [mx]
}
const price = {
template: `<input type='text' placeholder='单价' :value='price' @input='setPrice($event.target.value)' />`,
mixins: [mx]
}
const total = {
template: `<div>总价:{{total}}</div>`,
computed: {
// total() {
// 通过$store来获取Vuex的计算属性.
// return this.$store.getters.total
// }
// 通过映射方法快速引入Vuex的计算属性total
...mapGetters(['total'])
}
}
const App = {
template: `
<div>
<count />
<price />
<total />
</div>
`,
components: {count, price, total},
store
}
new Vue({
el: '#app',
components: { App },
template: `<App />`
})
</script>
6. Vuex的数据流
Vuex是如何让多个组件同时更新的?
- 实例化Vuex的过程中,Vue会给state内的数据都添加数据劫持.
- 通过挂载store实例,就可以知道当state内的数据变化时,应该更新多少个组件.(收集依赖).
- 每当state内的数据变化,都会更新对应组件的视图.
组件按钮 => actions(dispatch) => mutations(commit) => state => 自动通知对应的视图更新.
7. 总结
1:实例化,挂载
new Vuex.Store({
strict: true => 不允许在mutations之外修改数据
state: {} => 多个组件共用的数据(状态)
mutations => 修改state内数据的逻辑
actions => 异步触发mutations的方法
getters => state的计算属性
});
挂载 => 可以挂载到任意组件上.挂到new Vue上,就是全局状态管理.
挂到组件A上,则A组件和A组件的所有子孙元素都可以共享Vuex的数据.
2:获取数据
$store.state.数据名
computed: {
数据名() {
return this.$store.state.数据名
}
}
3:修改数据
没有异步 => 直接commit触发mutations
有异步 => 先通过dispatch触发actions,actions再触发mutations.
4:映射
mapState, mapMutations, mapActions, mapGetters