组件内状态管理流程
Vue最核心的两个功能:数据驱动和组件化
组件化开发给我们带来了:
- 更快的开发效率
- 更好的可维护性
每个组件都有自己的状态、视图和行为等组成部分
new Vue ({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
状态管理包含一下几部分:
-
state:驱动应用的数据源
-
view:以声明方式将state映射到视图
-
actions:响应在view上的用户输入导致的状态变化
组件间通信方式
大多数场景下的组件都并不是独立存在的,而是相互协作共同构成了一个复杂的业务功能。在 Vue 中为 不同的组件关系提供了不同的通信规则。
image-20210315195537146.png
组件通信方式主要有以下几种:
-
这里的props是以字符串数组形式列出的 prop,还可以进行类型检查和其它 prop 验证
// 父组件 <blog-post title="My journey with Vue"></blog-post> // 子组件 Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
-
<!-- 子组件 在子组件中使用 $emit 发布一个自定义事件--> <button v-on:click="$emit('enlargeText', 0.1)"> Enlarge text </button> <!-- 父组件 使用 v-on 监听这个自定义事件--> <blog-post v-on:enlargeText="hFontSize += $event"></blog-post>
-
// eventBus.js 使用 Event Bus 来解决非父子组件传值问题 export default new Vue() // 通信两端 使用$on订阅 使用$emit发布 // 有参数 bus.$on('自定义事件名称', data => { // 执行操作 }) // 有自定义传参 bus.$emit('自定义事件名称', 数据) // 没有参数 bus.$on('自定义事件名称', () => { // 执行操作 }) // 没有自定义传参 bus.$emit('自定义事件名称')
-
ref有两个作用:
- 如果作用在普通HTML标签上,则获取到的是DOM
- 如果作用在组件标签上,则获取到的是组件实例
// base-input组件 <template> <input ref="input"> </template> <script> export default { methods: { // 用来从父级组件聚焦输入框 focus: function () { this.$refs.input.focus() } } } </script> // 父组件中使用ref获取子组件实例 <base-input ref="usernameInput"></base-input> // 然后在父组件等渲染完毕后使用 $refs 访问 <script> mounted () { this.$refs.usernameInput.focus() } </script>
简易的状态管理方案
如果多个组件需要共享状态,使用上述方法可以实现,但是比较麻烦,而且多个组件之间互相传值很难追踪数据变化,问题难被定位。
当遇到多个组件需要共享状态时:典型场景如购物车,会遇到一下问题
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对兄弟组件间的状态传递无能为力,不宜管理
对于问题二,经常会采用父子组件直接引用或通过事件来变更和同步状态的多份拷贝,会导致代码难以维护
因此,我们为什么不把组件的共享状态抽出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或触发行为
可以把多个组件的状态,或者整个程序的状态放到一个集中的位置存储,并且可以检测到数据的变更,可以以一种简单的方式实现
// 1.创建共享仓库store对象 全局唯一对象
// 所有的状态都在store中管理,任意组件都可以导入并使用其中的状态,更改状态在store中实现;通过特殊手段,记录每次状态变化
export default {
debug: true, // 方便调试
state: {
user: {
name: 'xiaoming',
age: 12,
sex: '男'
}
},
setUserNameAction (name) {
if (this.debug) {
console.log('setUserNameACtion triggered:' + name)
}
this.state.user.name = name
}
}
// 2.将store对象存储到需要共享状态的组件的data中
<template>
<h1>{{ sharedState.user.name }}</h1>
</template>
import store from './store'
export default {
data () {
return {
privateState: {}, // 特有状态
sharedState: store.state // 共享状态
}
},
methods: {
// 点击按钮调用action修改状态
change () {
store.setUserNameAction('xiaoli')
}
}
}
接着继续延伸约定,组件不允许直接变更store对象的state,而应该通过触发action来分发(dispatch)事件通知store去改变,这样最终就会和Vuex结构类似。
约定的好处是:
- 记录所有store中发生的state变更
- 记录变更、保存状态快照、历史回滚/时光旅行等
网友评论