目录
1、组件跨层级访问
(1)$emit
(2)$root 、 $parent、$refs
2、依赖注入
3、透传及组件二次封装
组件间通信的三种方案: 1、组件跨层级访问,2、依赖注入,3、透传(用于组件二次封装)
1、组件跨层级访问
(1)$emit
严格按照单项数据流的规范来说,直接从子组件去修改父组件是不被允许的,但我们可以通过emit事件来通知父级组件进行相应的修改。在每一层都进行emit,这个过程很是枯燥和乏味,而且一旦传错,排查起来也不方便。
(2)$root 、 $parent、$refs
子组件可以通过$root 访问根组件,通过$parent访问父组件
vue在每个实例上够提供了$root 和 $parent 属性。可以通过$root访问当前单页应用的根组件,通过$parent来访问当前组件的父组件,相应的也就可以直接修改根组件或父组件的属性或是调用根组件或父组件上的方法。
父组件可以通过$ref访问子组件
$refs 只能在 mounted 生命周期钩子函数被调用之后才能使用。
$parent 和 $root 在各个生命周期钩子函数中都可以使用。
虽然使用$root 和 $parent 可以方便地修改内容,但这不可避免了造成了子组件和相应根组件或者父组件之间的强耦合,即使用了$parent的子组件必须和相应的父组件成对使用,缺一不可。
在组件化的设计中,像这类的组件不好扩展。
举例如下图:一对组件app 和 content, app是父 ,content是子,子组件 content 有一个say方法 this.$parent.fish.love
如果新增需求,要求在App2里面也有这么一个儿子组件content,那我们很自然的就会想到直接拿来用了,可这时出现了问题 直接调用 say 方法 this.$parent.fish is undefined, 因为新的App2没有这个变量。这就尴尬了,这就搞的我们在用一个组件的时候,要把这个组件全部审查一遍,非常耗费时间。
vue里面提供依赖注入的方式来解决这个问题。
2、依赖注入
依赖注入:声明了当前组件依赖的父组件们(直系的祖宗)的外部prop有哪些。
vue里面的provide和inject是对$parent的一种优化封装
provide和inject需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效
依赖注入实现原理:
出处:vue2.0源码,vue2.0/src/core/instance/inject.js
inject也是通过$parent, 依次往父组件上去寻找声明了的provide对象。
和prop类似,那为什么还需要provide / inject
呢?
因为在现实的项目中,我的一个组件嵌套着好几层组件,如果运用prop一层层的嵌套传递,就非常的麻烦。而provide / inject
就解决了这个问题,只要在顶层父级provide
里声明对象或方法,那么下一层级无论多深都能够通过inject
来访问到provide
的数据。
依赖注入的优点:
祖先组件不需要知道哪些后代组件使用它提供的属性
后代组件不需要知道被注入的属性来自哪里
依赖注入的缺点:
组件间的耦合较为紧密,不易重构,使得组件复用性受到影响。
提供的属性是非响应式。
无法追踪数据的来源,在任意层级都能访问导致数据追踪比较困难,不知道是哪一个层级声明了这个或者不知道哪一层级或若干个层级使用了
3、透传及组件二次封装
透传方案 $attr 和 $listener:通过 v-bind=“$attr” 来传递父组件上的prop class 和 style 通过 v-on=“$listenser” 来传递父组件上的事件监听器和事件修饰符
组件二次封装:对一些现有的组件或者三方的组件库做一些定制化的需求
举例:比如对el-input进行二次封装
子组件
<template>
<div>
<el-input v-bind="$attrs" v-on="$listeners"></el-input>
</div>
</template>
<script>
export default {};
</script>
<style lang="less" scoped>
/deep/ .el-input__inner {
border-top: none;
border-left: none;
border-right: none;
}
</style>
父组件
<template>
<div>
<s-input v-model="value" @blur="onBlur"></s-input>
{{ value }}
</div>
</template>
<script>
import SInput from "@/components/SInput.vue";
export default {
name: "attrs",
data() {
return {
value: "aaa"
};
},
components: {
SInput
},
methods: {
onBlur() {
console.log("blur");
}
}
};
</script>