在之前的文章中,我们讲到了 Vue 组件之间交互用单向数据流——通过 Props 向子组件传递数据,通过 emit 使用事件抛出一个值的模式。在组件上使用 v-model、sync还支持双向数据绑定,这是 Vue 的一个特性。本文将介绍 Vue 处理边界情况有关的组件访问功能。
访问父级组件实例
通过 $parent 属性
$parent属性可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。
// ByParent.vue
<template>
<button @click="handleClick">Call parents's method</button>
</template>
<script>
export default {
name: "ByParent",
methods: {
handleClick() {
this.$parent.updateParentNow();
}
}
};
</script>
在 ByParent
子组件中,通过 this.$parent
可以访问父组件的实例的 updateParentNow
方法。如果一个组件父组件和多个子组件组成,在确认父子组件关系的前提下,这种组件访问方式还是比较方便的。像Element UI 组件库中的一些开发群组组件,比如单选组、标签页、折叠面板等组件会用到这种模式。如果我们只是使用封装好的组件,基本上用不到这种模式。
在绝大多数情况下,触达父级组件会使得你的应用更难调试和理解,尤其是当你变更了父级组件的数据的时候。
访问子组件实例或子元素
使用 Prop 函数
在 React 中处理表单时,要类似实现 Vue 双向数据绑定效果,一般会向子组件传两个 Prop:一个 value 变量,另一个 onChange 处理函数。由于函数时引用类型。父子组件中的 onChange 处理函数 会保留同一个指针引用,因此子组件可以将其内部实例或元素通过Prop 处理函数传回父组件。其实,在 Vue 中也可以这样做。看下面示例:
- ByProp 组件
<template>
<div>
<div>{{ open }}</div>
<button @click="handleUpdate">Call child</button>
</div>
</template>
<script>
export default {
name: "ChildProp",
props: {
onChange: {
type: Function,
default: null,
},
},
methods: {
handleUpdate() {
this.now = Date.now();
this.onChange(this.now);
},
},
};
</script>
- 调用 ByProp
<fieldset>
<legend>Function Prop</legend>
<div>{{ propNow }}</div>
<ByProp :on-change="updatePropNow" />
</fieldset>
本实例中我们将 ByProp
组件实例的 this.now
传回父组件 App
并在其模板显示最新值。
使用 ref 属性
尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref 这个属性为子组件赋予一个 ID 引用。例如:
调用
<fieldset> <legend>$ref</legend> <ByRef ref="child" /> <button @click="$refs.child.updateNow()">Call child</button> </fieldset>
现在在你已经定义了这个 ref 的组件 App
里,你可以使用this.$refs.child 来访问这个 <ByRef》 实例方法 updateNow
。
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
监听状态 Prop
不像 $refs 那样强耦合,我们可以通过在父组件中动态改变传入子组件实例的状态 Prop,在子组件内部用 watch
监听该 Prop,如果有变化则改变子组件实例的值。如下示例所示:
- 子组件代码:
// ByWatch.vue
<template>
<div>{{ now }}</div>
</template>
<script>
export default {
name: "ByWatch",
props: {
toggled: {
type: Boolean,
default: false,
},
},
data() {
return {
now: Date.now(),
};
},
watch: {
toggled(newValue) {
this.updateNow();
},
},
methods: {
updateNow() {
this.now = Date.now();
},
},
};
</script>
ByWatch
组件中,我们监听了从父组件中传入的 toggled
prop,toggled值变化时会调用 updateNow
方法更新当前时间戳。
- 父组件调用
在父组件 App
中这样实例化 ByWatch
组件:
<fieldset>
<legend>Watch Prop</legend>
<ByWatch :toggled="toggled" />
<button @click="toggled = !toggled">Call child</button>
</fieldset>
ByWatch
组件中,传入了 toggled
prop,并通过切换按钮来模拟改变 toggled
值 。
监听状态 Prop 适合子在子组件实例未完全挂载时为其异步完成清理,一个真实案例就是,每次打开模态框时重置Form。
使用 Vuex
Vuex 作为专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 只用在其他模式不好时的场景下使用,并不是真正少用的组件访问方式。相反,他是一个通用方案。不仅能访问父级组件实例,也能好呢方便的访问子组件实例或子元素。
父组件调用
<button @click="$store.commit('UPDATE_NOW')">Call child</button>
App
父组件中,通过上点击按钮提交了 MutationUPDATE_NOW
。- 子组件代码:
// ByVuex.vue
<template>
<div>{{ now }}</div>
</template>
<script>
export default {
name: "ByVuex",
data() {
return {
now: Date.now(),
};
},
mounted() {
this.func = this.$store.subscribe((mutation) => {
if (mutation.type === "UPDATE_NOW") {
this.updateNow();
}
});
},
beforeDestroy() {
this.func();
},
methods: {
updateNow() {
this.now = Date.now();
},
},
};
</script>
ByVuex
组件中,订阅 store 的 mutation。updateNow
方法会在 mutation UPDATE_NOW
完成后调用。
Vuex 中的发布/订阅模式大大降低了组件间、模块间的耦合度,值得试一试。
评论 (0)