VUE状态管理
状态就是响应式的数据和呈现它的视图
组件的状态由它的数据和视图决定的
状态管理 = 数据和视图管理
如何实现多个组件共用一个状态?
- 把数据和修改数据的逻辑都放在某个祖先组件上,子组件触发祖先组件的修改逻辑
- Vuex(vue的插件 = Vue的全局状态管理)
Vuex的插件4xx版本是为了配合Vue3xx版本
Vue的版本如果是2xx,则Vuex的版本应该是用3xx
Vuex
Vuex的数据流
组件按钮 => actions(dispatch) => mutations(commit) => state => 自动通知对应的视图更新.
实例化Vuex数据仓库
实例化
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 0
}
})
挂载,如果挂载到new Vue上,就是全局状态管理,所有组件都可以访问state的数据
如果挂载到组件A,则A和其子孙组件可以使用
new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
// 挂载到new Vue上
store
});
获取Vuex数据
挂载完成后,vue组件内,可以通过this.$store.state
来获取这个state的实例
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button>box--count++</button>
</div>
`,
computed: {
// 第一种取名字的写法
count() {
return this.$store.state.count
}
},
}
const son = {
template: `
<div>
// 第二种写法,这没有this
<h3>son组件---{{$store.state.count}}</h3>
<button>son--count++</button>
</div>
`,
}
修改Vuex数据
同步
修改state的数据,需要通过mutations选项写在数据仓库中,可以写多个方法
mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数。
组件触发mutations内的方法,需要通过this.$store.commit
触发
commit("mutations的方法名",组件实参)
commit内是不能有异步操作
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方法名’,组件实参),
actions内的方法,第一个参数是store本身,第二个是实参
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
}
},
}
把Vuex的数据和逻辑映射成组件的数据和逻辑
map映射:
- 把state的数据变成组件数据
- 把mutations和actions的方法变成组件方法
const { mapState, mapMutations, mapActions } = Vuex;
mapState
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'
})
},
}
mapMutations,mapActions
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='asyncPlus(1)'>box--count++</button>
<button @click='reduce(1)'>box--count--</button>
</div>
`,
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['asyncPlus']),
...mapMutations(['reduce'])
}
}
组件混合
组件公共选项部分
需要通过mixins选项引入
如果组件本身就包含mx内的选项,则以组件的选项为准
const mx = {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['asyncPlus']),
...mapMutations(['reduce'])
}
}
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='asyncPlus(1)'>box--count++</button>
<button @click='reduce(1)'>box--count--</button>
</div>
`,
mixins: [mx],
}
Vuex的计算属性
computed: {
// total() {
// 通过$store来获取Vuex的计算属性.
// return this.$store.getters.total
// }
// 通过映射方法快速引入Vuex的计算属性total
...mapGetters(['total'])
}
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 />`
})
组件的data为什么必须是函数,因为组件的状态应该是相互独立,组件复用时,函数返回。新的对象,新的对象对应新的状态。
$refs
用于获取视图标签
方法一,写在标签里:
- 在标签里
ref='一个名字'
- 使用的时候
this.$refs.一个名字.style.backgroundColor = 'red'
方法二,写在组件里
<组件名 ref='一个名字' />
this.$refs.box返回一个vue实例
$forceUpdate
强制更新当前组件,应急性api,不推荐滥用
$children和$parent
$children[i].xxx 修改当前组件的子组件的属性(方法),i表示下标
$parent .xxx 修改当前组件的父组件的属性(方法)