Vue.js 组件状态管理
Vue 中最核心的两个功能分别是数据驱动和组件化。组件化可以提高开发效率和代码的可维护性。
new Vue({
// state
data () {
return {
count: 0
}
},
//view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
状态管理
- state, 数据驱动的数据源
- view,以声明方式将 state 映射到视图
- actions,响应在 view 上的用户输入导致的状态变化
组件间通信方式
- 父组件给子组件传值
- 子组件给父组件传值
- 不相关组件之间传值
父组件给子组件传值
- 子组件通过 props 接收数据
- 父组件中给子组件通过相应属性传值
<child title="hello"></child>
<script>
import child from @/component/child.vue
export default {
component: {
child
}
}
</script>
export default {
// props: ['title']
props: {
title: String
}
}
子组件给父组件传值
<template>
<div>
<h1 :style="{ fontSize: hFontSize + 'em' }">Event Up Parent</h1>
这里的文字不需要变化
<child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
<child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
<child :fontSize="hFontSize" @enlargeText="hFontSize += $event"></child>
</div>
</template>
<script>
import child from './02-Child'
export default {
components: {
child
},
data () {
return {
hFontSize: 1
}
},
methods: {
enlargeText (size) {
this.hFontSize += size
console.log(this.hFontSize)
}
}
}
</script>
<template>
<div>
<h1 :style="{ fontSize: fontSize + 'em' }">Event Up Child</h1>
<button @click="handler">文字增大</button>
</div>
</template>
<script>
export default {
props: {
fontSize: Number
},
methods: {
handler () {
this.$emit('enlargeText', 0.1)
}
}
}
</script>
不相关组件传值
需要创建一个 Vue 实例作为事件总线或者事件中心
import Vue from 'vue'
export default new Vue()
<template>
<div>
<h1>Event Bus Sibling01</h1>
<div class="number" @click="sub">-</div>
<input type="text" style="width:30px; text-align:center" :value="value">
<div class="number" @click="add">+</div>
</div>
</template>
<script>
import eventbus from './eventbus'
export default {
props: {
num: Number
},
created () {
this.value = this.num
},
data () {
return {
value: -1
}
},
methods: {
sub () {
if (this.value > 1) {
this.value--
eventbus.$emit('numchange', this.value)
}
},
add () {
this.value++
eventbus.$emit('numchange', this.value)
}
}
}
</script>
<style>
.number {
display: inline-block;
cursor: pointer;
width: 20px;
text-align: center;
}
</style>
<template>
<div>
<h1>Event Bus Sibling02</h1>
<div>{{ msg }}</div>
</div>
</template>
<script>
import eventbus from './eventbus'
export default {
data () {
return {
msg: ''
}
},
created () {
eventbus.$on('numchange', value => {
this.msg = `您选择了${value}件商品`
})
}
}
</script>
其他常见方式
- $root
- $parent
- $children
- refs
ref 的作用
- 在普通的 html 标签上使用 ref ,获取到的是 DOM
- 在组件上使用 ref ,获取到的是组件实例
<template>
<div>
<h1>ref Parent</h1>
<child ref='c'></child>
</div>
</template>
<script>
import child from './04-Child'
export default {
components: {
child
},
mounted () {
this.$refs.c.focus()
this.$refs.c.value = 'hello input'
}
}
</script>
<template>
<div>
<h1>ref Child</h1>
<input ref="input" type="text" v-model="value">
</div>
</template>
<script>
export default {
data () {
return {
value: ''
}
},
methods: {
focus () {
this.$refs.input.focus()
}
}
}
</script>
ref 传值这种方式要慎用!容易造成状态管理混乱
简易的状态管理方案
如果多个组件之间需要共享状态或者说数据,使用前面演示的方式,虽然都可以实现,但是比较麻烦,而且多个组件之间互相传值,很难跟踪到数据的变化,容易造成数据的混乱,出现问题难以定位。
- 问题
- 多个视图依赖同一个状态
- 来自不同视图的行为需要变更同一状态
为了解决这些问题,可以把不同组件之间依赖的共同的状态抽取出来,存储到一个全局对象中,并且将来使用的时候保证他是响应式的。这个对象创建之后,任何组件都可以获取或修改全局对象中的状态。
// store
export default {
debug: true,
state: {
user: {
name: 'xiaodong',
age: 18,
sex: '男'
}
},
setUserNameAction (name) {
if (this.debug) {
console.log('setUserNameAction triggered:', name)
}
this.state.user.name = name
}
}
<!-- ComponentA -->
<template>
<div>
<h1>ComponentA</h1>
user name: {{ sharedState.user.name }}
<button @click="change">Change Info</button>
</div>
</template>
<script>
import store from './store'
export default {
data () {
return {
privateState: {},
sharedState: store.state
}
},
methods: {
change () {
store.setUserNameAction('componentA')
}
}
}
</script>
<!-- ComponentB -->
<template>
<div>
<h1>ComponentB</h1>
user name: {{ sharedState.user.name }}
<button @click="change">Change Info</button>
</div>
</template>
<script>
import store from './store'
export default {
data () {
return {
privateState: {},
sharedState: store.state
}
},
methods: {
change () {
store.setUserNameAction('componentB')
}
}
}
</script>