【Vue2】父子、兄弟组件通信遇到的问题汇总

遇到问题首先要查官方文档

https://v2.cn.vuejs.org/v2/guide/components-edge-cases.html

文档能首先解决60% - 80%的问题。

0.了解子组件与父组件的关系

  • 加载渲染过程

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

  • 子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

  • 父组件更新过程

父beforeUpdate->父updated

  • 销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

导入子组件
在这里插入图片描述

1.父组件调用子组件内容【一对一】

首先这个一对一表示的是一个父组件下同名的只有一个子组件。【即子组件不可以被v-for包围】

1.1 父组件调用子组件的methods

  1. 指定名称,ref是组件标签自带的一个属性,直接写就好了,表示被引用【reference】时的名称。
    在这里插入图片描述

  2. 在js栏中调用子组件的方法,$refs是Vue对象的一个实例属性,存储当前vue对象中所引用的组件标签。我们刚刚指定了子组件的名称,直接对应然后就可以调用该名称组件内部的方法了。

在这里插入图片描述
无论是方法1还是放发2,多关注是否使用了v-if在子组件上,v-if为false的时候,未渲染组件,无法通信。

方法2:

方案二:通过组件的$emit、$on方法;

复制代码
//父组件中

<template>
    <div>
        <Button @click="handleClick">点击调用子组件方法</Button>
        <Child ref="child"/>
    </div>
</template>    

<script>
import Child from './child';

export default {
    
    
    methods: {
    
    
        handleClick() {
    
    
               this.$refs.child.$emit("childmethod")    //子组件$on中的名字
        },
    },
}
</script>

//子组件中

<template>
    <div>我是子组件</div>
</template>
<script>
export default {
    
    
    mounted() {
    
    
        this.$nextTick(function() {
    
    
            this.$on('childmethods', function() {
    
    
                console.log('我是子组件方法');
            });
        });
     },
};
</script>

以上两种方法都是基于this.$refs调用子组件,要求子组件已渲染,如果子组件使用了v-else等导致无法立即渲染的,可能会失效。【输出this.$refs可以看到有哪些子组件】,建议换用v-show。

2.子组件调用父组件内容【一对一】

一个子组件只有一个父组件

2.1 子组件调用父组件的methods

子组件直接使用this.$parent.xxx()即可,注意方法名是其父组件要已经定义的。
在这里插入图片描述
(效果有点不好)

在一对一的父子通信下,该方法更加简单。当然有另一种方法是使用$emit,下面的多对一中会讲到。

2.2 子组件调用父组件的属性

  1. 指定名称,即指定ref

  2. 先进入子组件,子组件中指定需要的变量的名称,指定字符串名称即可,因为只作为该组件的属性名!同时要注意,props是一个数组,而且是官方指定的vue实例的一个属性,所以其级别是和methods和data是同级的,不要放到里面去了。而且这个是在子组件中定义!

  3. 这里的item和i都是for内的临时值,没有在父类的return中定义。如果绑定this.父类的属性。则要求props中提供的变量名不能与之重复。比如父类return {num: 10}; 子类中不允许props: {‘num’}; 要改为props: {‘myNum’}
    在这里插入图片描述

  4. 在进入父组件,对于子组件需要访问的变量,我们直接双向绑定,属性名与子组件的props数组中定义的一致,而绑定的对象就是父组件的属性变量,可以是如图的v-for的对象,也可以是data中的变量对象。【如果出错了,检查自己是不是加了this.属性,一律不要加this.属性, 直接输入变量名就好了】

如图:【先别看@loadComments,下面会介绍】
在这里插入图片描述

  1. 进入子组件,直接使用变量item

在这里插入图片描述

这里的值是可以直接在method中使用的,在method中监察可以使用watch。而如果需要修改父组件的值,最好在父组件添加一个方法,使用method调用在父组件中进行修改

注意:

vue 中父组件向子组件传递数据用 props, 但是子组件是无法修改它的。如果子组件需要动态修改它就只能自造一个对应的 data 域。比如

components:{Tinymce},
  props:['id','formDatas'],
  data() {
    return {
      form: {
        title: '',
        title_type: '',
        push_date: '',
        source: '',
        title_introduce: '',
        title_content:'',
      }
    }
  },
  created(){
    this.form = this.formDatas
  },

这样是无法把props的值传递给data里面,因为data()只会运行一次,所以要用watch来进行监听props里面内容的变化,然后对data里面进行赋值
【是news而不是new】

 watch:{
    formDatas(news,olds){
      this.form = news
    }
  }

因此当父组件传递值给子组件的时候,watch就会监听到formDatas的变化,将新的值赋给你想要传值的data,然后进行改变。

3.父组件调用子组件【一对多】

一个父组件下有多个同名子组件。如图,通过v-for后,名为Comments的组件在当前父组件中可就不止一个了。

在这里插入图片描述

3.1 父组件调用子组件的methods

  • 如果我们仍然使用this.$ref.xxx 肯定无法获取,因为有多个同名组件。

  • 那很容易理解,这多个同名对象一定被撞到一个容器中了。即this.$ref.xxx[i]不就行了!?如图官方文档

在这里插入图片描述

  • 从理论上确实可以,但是实际上,我尝试这样去获取的时候,发现这个容器【应该是个set】中的元素是无序的,我每次获取Comment[0],得到的vue对象都时不一样的。如图:

在这里插入图片描述

  • 所以如果是在v-for下的情况,是不可以使用this.$ref.xxx[i]来获取子组件对象的
  • 并且官方文档也警示我们,不要在v-for的场景下使用$refs获取子组件。
    • 解释一下官方文档。v-for和v-if一样,指令都是响应了数据变化再渲染的。而如果对数据的修改不是响应式的,那么v-for和v-if就不会自动重新渲染,即使数据变更了。比如你发表了一个帖子,如果对数据进行非响应式修改,那么页面不会出现新帖子的数据。
    • 所以$refs访问子组件是一种非响应式访问,应该避免在需要响应式的场景中使用。

在这里插入图片描述

  • 因此!在响应式场景【v-for、v-if】目前不建议父组件去调用子组件的方法,本来就可以单独交给子组件做的就在子组件中完成。
  • 也不建议使用this.$ref.xxx[i],因为对于索引来说会失控

解决方向参考:

  • 最优的解决方向是,对于子组件自己完成该功能所需要的参数,由父组件提供【即指定props数组】即可,由子组件自己完成自己的方法。父组件尽量不要去调用子组件的方法。
  • 比如说把触发响应时间的DOM从父组件剥离去到子组件,避免父组件响应然后去调用子组件。

4.子组件调用父组件【多对一】

即多个同名子组件对应一个父组件。

4.1 子组件调用父组件的属性

与**子组件调用父组件的属性【一对一】**一致,使用props数组

4.2 子组件调用父组件的methods

  1. 同子组件要访问属性一样,变量数据是双向绑定,那调用方法就是一个事件
  2. 但是要注意,目前对于子组件的事件,不需要像props数组一样提前定义
  3. 首先我们进入父组件,对子组件对象添加一个触发事件,事件名称与要调用的父组件方法同名即可,如图的loadComments

在这里插入图片描述

  1. 然后我们进入子组件,再需要使用父组件方法的地方使用 e m i t 属性,其语法规则为 ‘ t h i s . emit属性,其语法规则为`this. emit属性,其语法规则为this.emit(事件名,参数1,参数2,…)`,只需要第一个参数是事件名即可,其余的参数个数不定,根据父组件方法的参数个数进行传参。

在这里插入图片描述

补充:bus的使用

官方对于兄弟组件进行通信,使用的是$bus

总结【一般使用】

  • 父调用子

    • 父组件调用子组件的属性:一般不会调用
    • 父组件调用子组件的方法:使用$refs.子组件的ref名.方法名调用
  • 子调用父

    • 子组件调用父组件属性:先在子组件中定义props数组指明数据名,然后在父组件中对子组件标签进行双向绑定。
    • 子组件调用父组件方法:
      • 子组件直接使用this.$parent.方法名调用
      • 父组件中对子组件标签进行事件绑定父组件的方法,然后子组件中使用$emit()方法触发事件
  • 如果父子通信出现在v-for的场景下

    • 父组件无法调用子组件的方法,因为$refs的索引不可控
    • 子组件调用父组件方法不可以使用非响应式的this. p a r e n t . 方法名,而需要使用 parent.方法名,而需要使用 parent.方法名,而需要使用emit
    • 子组件调用父组件的属性的方式不变

猜你喜欢

转载自blog.csdn.net/NineWaited/article/details/126334211