vue的父子孙之间组件通信和兄弟之间的组件通信

父子孙组件之间的组件通信

  • 面试题:vue组件之间有哪些通信方式呢?

第一种:props+this.$emit:

props:

父组件A通过v-bind绑定数据,传递数据给子组件,子组件用props接收

父组件A:

<template>
  <div class="parent-root">
    <child class="child"  :message="message"> </child>
  </div>
</template>

<script>
import child from "@/components/child";
export default {
  name: "parent",
  components: { child },
  data() {
    return {
      message:"霸霸的数据"
    };
  },
     </script>

子组件B:

<template>
  <div class="child-root">
    父组件传来的消息:{{message}}   
  </div>
</template>

<script>
  export default {
    name: 'child',
    props: ['message'],
 </script>
this.$emit:

父组件可以在使用子组件的地方直接用 v-on来监听子组件触发的事件,使用 v-on绑定自定义事件eventName,然后在子组件中使用 $emit(eventName,data)触发事件

父组件A:

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

在这里插入图片描述

第二种:style、class和自定义属性

父组件可以在子组件标签上添加style、class和自定义属性,子组件会合并到自己的根标签身上

父组件A:

<template>
  <div class="parent-root">
     <child class="child" style="color:#ccc" datd-a="5" :message="message"> </child>
  </div>
</template>

<script>
import child from "@/components/child";
export default {
  name: "parent",
  components: { child },
  data() {
    return {
      message:"霸霸的数据"
    };
  },
     </script>

子组件B:

<template>
  <div class="child-root">
    父组件传来的消息:{{message}}   
  </div>
</template>

<script>
  export default {
    name: 'child',
    props: ['message'],
 </script>

第三种:this.$attrs、this.$listeners和inheritAttrs

  • 父子组件间的this.$attrs存放了父组件传过来的除style、class、自定义属性和props接收的属性之外的其他属性,通常配合 interitAttrs 选项一起使用
  • 需要注意的是:子组件里使用props接收父组件传来的属性数据之后,该属性就不在this.$attrs里面存放了
  • 父孙之间可以使用this.$attrs传递数据,就不需要在每一层的子组件里都使用v-bind和props层层传递了
  • this.$listeners存放了父组件用v-on(不含 .native 修饰器)的绑定的所有事件,子组件可通过v-bind:$listeners将所有事件传递给后面的孙组件
  • inheritAttrs:默认为true, 会自动在挂载组件元素上属性值,如props声明了属性,则挂载未被声明的属性;false时会关闭自动挂载到组件根元素上属性。注意:这个选项不影响 class 和 style 绑定。

父组件:

<template>
  <div class="parent-root">
    <child class="child" :name="name" :age="age" :data="data" @change="change" @childEvent="childEvent"> </child>
    <p>孙组件传出来的值:{{text}}</p>
  </div>
</template>

<script>
  import child from "@/components/child";
  export default {
    name: 'parent',
    components: {child},
    data() {
      return {
        name:"小明",
        age:18,
        text:'',
        data:"me"
      };
    },
    methods: {
      //孙组件传出来的事件
      change(text) {
        this.text = text;
      },
      childEvent(name){
console.log("name:",name);
      }
    },
  };
</script>

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

  • inheritAttrs:

    • inheritAttrs为true时:
      在这里插入图片描述
    • inheritAttrs为false时:
      在这里插入图片描述

第四种: this.$parent和 this.$children

在组件内部,可以通过$parent$children属性,分别得到当前组件的父组件和子组件实例在这里插入图片描述
结果:
在这里插入图片描述

第五种:ProvideInject

父组件实例可以使用Provide向后代组件传递数据,后代组件使用inject来接收数据,

但是需要注意的是,使用这个方法传递的数据不是响应式的,然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

详细看:https://cn.vuejs.org/v2/api/?#provide-inject

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

第六种:v-model

v-model 专门用来做表单元素的当绑定在表单元素上时

  • 当表单为输入表单,v-model最后会生成一个value属性和input事件
  • 当绑定的是一个多选或者单选框,v-model最后生成一个checked属性和change事件

v-model指令实质是一个语法糖,它是value属性和input事件的结合体

<input :value="data" @input="data=$event.target.value" />
<!-- 等同于 -->
<input v-model="data" />

详见:表单输入绑定

$event.target.value
获取触发事件的target,就是触发事件的DOM元素的value

**当v-model绑定在组件上时 ( 等同于prop+this.$emit ) **

  • 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
<!-- 父组件 -->
<template> 
    <Child v-model="value" />
</template>
等同于:
<template> 
    <Child :value="value" @ />
</template>
<!-- 子组件 -->
<template>
	<input v-model="input" />
</template>
<script>
export default {
  props: {
    value: String,
  },
  model: {
    prop: 'value',		// 指定 v-model 要绑定的参数叫什么名字,来自于 props 中定义的参数
    event: 'change',	// 指定要触发的事件名字,将被用于 $emit
  },
  computed: {
    input: {
      // 这里的计算属性使用了 getter、setter,可以简化代码
      // 可参见链接 https://cn.vuejs.org/v2/guide/computed.html#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E7%9A%84-setter
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('change', val);	// 触发
      }
    }
  }
}
</script>

第七种:.sync修饰符

v-model的作用类似,用于双向绑定,不同点在于v-model只能针对一个数据进行双向绑定 ,只针对input事件的value值进行绑定,而sync修饰符没有限制

示例

<!-- 父组件 -->
<template>
  <div id="app">
    <Numbers :num1.sync="n1" :num2.sync="n2" />
    <!-- 等同于 -->
    <Numbers
      :num1="n1"
      @update:num1="n1 = $event"
      :num2="n2"
      @update:num2="n2 = $event"
    />
  </div>
</template>

<script>
import Numbers from "./components/Numbers.vue";

export default {
  components: {
    Numbers,
  },
  data() {
    return {
      n1: 0,
      n2: 0,
    };
  },
};
</script>

第八种:$ref

父组件可以通过ref获取到子组件的实例

  • 不过这种做法一般是不得已的时候才会使用 r e f , 因 为 ref,因为 refref会动用真实的DOM操作,与vue的减少对DOM元素的手动操作的理念有所违背

不局限于父子孙层级关系的跨组件通信:

(兄弟与兄弟)

第一种:vuex

适用于大型项目的数据仓库

第二种:store模式

适用于中小型项目的数据仓库

// store.js
const store = {
  loginUser: ...,
  setting: ...
}

// compA
const compA = {
  data(){
    return {
      loginUser: store.loginUser
    }
  }
}

// compB
const compB = {
  data(){
    return {
      setting: store.setting,
      loginUser: store.loginUser
    }
  }
}

第三种:eventbus

组件通知事件总线发生了某件事,事件总线通知其他监听该事件的所有组件运行某个函数 (发布订阅模式)【vue3.0好像不支持事件总线的方式了?】

// 发送消息
EventBus.$emit(channel: string, callback(payload1,))
 
// 监听接收消息
EventBus.$on(channel: string, callback(payload1,))

它的工作原理是发布/订阅方法,通常称为 Pub/Sub

var EventBus = new Vue();
Object.defineProperties(Vue.prototype, {
  $bus: {
    get: function () {
      return EventBus
    }
  }
})

在这个特定的总线中使用两个方法 $on$emit 。一个用于创建发出的事件,它就是 $emit ;另一个用于订阅 $on

var EventBus = new Vue();
this.$bus.$emit('nameOfEvent', { ... pass some event data ...});
this.$bus.$on('nameOfEvent',($event) => {
  // ...
})

然后我们可以在某个Vue页面使用 this.$bus.$emit("sendMsg", '我是web秀');,另一个Vue页面使用

this.$bus.$on('updateMessage', function(value) {
    console.log(value); // 我是web秀
})

同时也可以使用this.$bus.$off('sendMsg')来移除事件监听。

第四种:router

如果一个组件改变了地址栏,所有监听地址栏的组件都会做出相应反应

最常见的场景就是通过点击router-link组件改变了地址,router-view组件就渲染其他内容

在这里插入图片描述

笔记相关的代码:

父组件:

<template>
  <div class="parent-root">
    <child
      class="child"
      :name="name"
      :age="age"
      :data="data"
      @change="change"
      @childEvent="childEvent"
    >
    </child>
    <p>孙组件传出来的值:{{ text }}</p>
  </div>
</template>
<script>
import child from "@/components/child";
export default {
  name: "parent",
  components: { child},
  provide: {
    foo: "bar",
  },
  data() {
    return {
      name: "小明",
      age: 18,
      text: "",
      data: "me",
    };
  },

  computed: {},
  created() {},
  methods: {
    //孙组件传出来的事件
    change(text) {
      this.text = text;
    },
    childEvent(name) {
      console.log("name:", name);
    },
  },
};
</script>


<style scoped lang="less">
.parent-root {
  padding: 20px;
  font-size: 16px;
  position: absolute;
  p {
    margin-bottom: 10px;
  }
}
</style>

子组件:

<template>
  <div class="child-root">
    <grandchild v-bind="$attrs" v-on="$listeners"> </grandchild>
    <br />
    <button @click="childEvent">子组件的childEvent</button>
    <br />
    子组件用props接收了data的数据: {{ data }}
  </div>
</template>

<script>
import grandchild from "@/components/grandchild";
export default {
  name: "child",
  components: { grandchild },
  inject: ["foo"], 
  props: ["data"],
  //inheritAttrs:
  //默认为true, 会自动在挂载组件元素上属性值,如props声明了属性,则挂载未被声明的属性
  //为false时会关闭自动挂载到组件根元素上属性
  inheritAttrs: false,

  data() {
    return {};
  },
  computed: {},
  created() {
    console.log(this.foo);
    console.log("$attrs:", this.$attrs, "$listeners:", this.$listeners); //$attrs: { "name": "小明", "age": 18 },这里不会输出"class":"child"
  },
  mounted() {
    console.log("parent:", this.$parent, "children:", this.$children);
  },
  methods: {
    childEvent() {
      console.log("child的data:", this.data);
    },
  },
};
</script>

<style scoped lang="less">
.child-root {
 border: 1px solid #ccc;
}
</style>

孙组件:

<template>
  <div class="grandchild-root">
    <p>name:{{name}}</p>
    <p>age:{{age}}</p>
    <p>data:{{data}}</p>
    <button @click="bindChange">孙子点击传给爷爷</button>
  </div>
</template>
<script>
  export default {
    name: 'grandchild',
    components: {},
    props: {
      name: {
        type: String,
        default: ''
      },
      age:{
        type: Number,
        default: null
      },
      data:{
        type: String,
        default: null
      }
    },
    methods: {
      bindChange() {
        let text = '爷爷,您好'
        this.$emit('change',text);
      }

    },
  };
</script>

整理笔记看的相关文章:

vue的通信:

https://blog.csdn.net/zhoulu001/article/details/79548350

https://blog.csdn.net/Dobility/article/details/110147985

https://blog.csdn.net/weixin_42211816/article/details/116529864

v-model原理:

https://blog.csdn.net/yun_hou/article/details/86313212

https://blog.csdn.net/weixin_42380658/article/details/86568666

修饰符:

https://blog.csdn.net/qq_29468573/article/details/80771625

事件总线:

https://blog.csdn.net/i168wintop/article/details/95107935

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值