vue组件间通讯方式

1、父组件向子组件传值

父组件通过props属性的方式向子组件传递数据。props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

// 父组件
<template>
  <div>
    <child :name-list="nameList"></child>
  </div>
</template>
<script>
import comArticle from './Child.vue'
export default {
    
    
  components: {
    
     Child },
  data() {
    
    
    return {
    
    
      nameList: ['小明', '小强', '小张']
    }
  }
}
</script>

// 子组件 Child.vue
<template>
  <div>
    <span v-for="(name, index) in nameList" :key="index">姓名:{
    
    {
    
    name}}</span>
  </div>
</template>
<script>
export default {
    
    
  props: ['nameList']
  // 或者
  props: {
    
    
    nameList: {
    
    
      type: Array,
      default: () => {
    
    
        return []
      },
    },
  }
}
</script>

单向数据流 :所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定,父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
如果在一个子组件内部改变 prop,Vue 会在浏览器的控制台中发出警告。

2、子组件向父组件传值

子组件向父组件传值可以通过$emit实现,子组件通过$emit绑定一个事件,当事件触发时会将参数传递给监听器回调,父组件通过v-on绑定这个事件监听器接收参数。

// 父组件
<template>
  <div>
    <child :@update-name="getName"></child>
  </div>
</template>
<script>
import comArticle from './Child.vue'
export default {
    
    
  components: {
    
     Child },
  methods: {
    
    
    getName(name) {
    
    
      console.log('from child', name)
    }
  },
}
</script>

// 子组件 Child.vue
<template>
  <div>
    <span v-for="(name, index) in nameList" :key="index" @click="sendName(name)">姓名:{
    
    {
    
    name}}</span>
  </div>
</template>
<script>
export default {
    
    
  props: ['nameList']
  // 或者
  props: {
    
    
    nameList: {
    
    
      type: Array,
      default: () => {
    
    
        return []
      },
    },
  },
  methods: {
    
    
    sendName(name) {
    
    
      this.$emit('update-name', name)
    }
  },
}
3、$parent / $children

$parent可以访问到当前组件的父实例,$children可以访问到当前实例的直接子组件。

// 父组件
<template>
  <div>
    <child :name-list="nameList"></child>
    <button @click="changeChild">点击改变子组件值</button>
  </div>
</template>
<script>
import comArticle from './Child.vue'
export default {
    
    
  components: {
    
     Child },
  data() {
    
    
    return {
    
    
      nameList: ['小明', '小强', '小张'],
      age: 20
    }
  },
  methods: {
    
    
      changeChild() {
    
    
        // 获取到子组件
        this.$children[0].title = '修改了子组件标题'
      }
  }
}

// 子组件 Child.vue
<template>
  <div>
    <h1>{
    
    {
    
    title}}</h1>
    <span v-for="(name, index) in nameList" :key="index">姓名:{
    
    {
    
    name}}</span>
    <h1>父组件的值: {
    
    {
    
    parentData}}</h1>
  </div>
</template>
<script>
export default {
    
    
  props: ['nameList']
  // 或者
  props: {
    
    
    nameList: {
    
    
      type: Array,
      default: () => {
    
    
        return []
      },
    },
  },
  data() {
    
    
    return {
    
    
      title: '我是子组件的标题'
    }
  },
  computed:{
    
    
    parentData(){
    
    
      return this.$parent.age;
    }
  }
}

$parent获取的是父组件实例,如果没有则返回的是undefined
$children是获取当前实例的直接子组件,返回的格式是数组形式,如果没有子组件则返回一个空数组

4、$refs

$refs: 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。ref如果绑定在普通DOM元素上,指向这个DOM元素;ref如果绑定在组件上,指向该组件实例。

// 父组件
<template>
  <div>
    <child ref='childRef'></child>
    <button @click="getChild">点击获取子组件实例</button>
  </div>
</template>
<script>
import comArticle from './Child.vue'
export default {
    
    
  components: {
    
     Child },
  methods: {
    
    
      getChild() {
    
    
        // 获取到子组件
        const childRef = this.$refs.childRef
        console.log(childRef.title);  // 是子组件的标题
      }
  }
}

// 子组件 Child.vue
<template>
  <div>
    <h1>{
    
    {
    
    title}}</h1>
  </div>
</template>
<script>
export default {
    
    
  data() {
    
    
    return {
    
    
      title: '我是子组件的标题'
    }
  },
}
5、provide / inject (2.2.0 新增)
  • provide:Object | () => Object
  • inject:Array<string> | { [key: string]: string | Symbol | Object }

provide / inject 需要一起使用,父组件通过privide 提供变量,子孙组件通过inject 注入这个变量依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
缺点:
官网不建议在应用中直接使用该办法,理由很直接:他怕你"管不好"

设计项目,通常追求有清晰的数据流向和合理的组件层级关系便于调试和维护,然而这对选项支持任意层级都能访问,导致数据追踪比较困难。不知道那一层级声明了provide又或是哪些层级使用了inject。造成比较大的维护成本。因此,除组件库或高阶插件外,Vue建议用Vuex解决或其他办法处理

// 父级组件提供 'foo'
var Provider = {
    
    
  provide: {
    
    
    foo: 'bar'
  },
  // 函数 provide
  provide() {
    
    
    return {
    
    
      foo: this // 将实例传递下去, 子组件可以获得响应式属性
    }
  }
  // ...
}

// 子组件注入 'foo'
var Child = {
    
    
  inject: ['foo'],
  created () {
    
    
    console.log(this.foo) // => "bar"
  }
  // ...
}

提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
provide就相当于加强版父组件prop,可以跨越中间组件,inject就相当于加强版子组件的props
对于一些简单的数据可以使用provide / inject

6、$attrs / $listeners

使用 v-bind=”$attrs”, 将父组件中不被认为 props 特性绑定的属性传入子组件中,通常配合 interitAttrs 选项一起使用。之所以要提到这两个属性,是因为两者的出现使得组件之间跨组件的通信在不依赖 vuex 和 事件总线 的情况下变得简洁,业务清晰。

inheritAttrs: 默认值 true,继承所有的父组件属性(除 props 的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置 inheritAttrs: false ,但是 class 属性会继承。
(简单的说,inheritAttrs:true 继承除 props 之外的所有属性;inheritAttrs:false 只继承 class style 属性

$attrs
$attrs–继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上2.4.0 新增
类型:{ [key: string]: string }
详细:
包含了父作用域中 不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind=”attrs”, 传入内部组件——在创建高级别的组件时非常有用。

$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件——在创建更高层次的组件时非常有用。

// parent
<template>
  <div class="main">
    <child
      child="父组件"
      placeholder="占位符"
      @parentMethods="onMethods1($event)"
    ></child>
  </div>
</template>
<script>
import Child from "./child.vue";
export default {
    
    
  name: "Login",
  components: {
    
     Child },
  methods: {
    
    
    onMethods1(e) {
    
    
      console.log(e);
    }
  },
};
</script>

// child
<template>
  <div>
    <h1>child</h1>
    <p style="color: red">$attrs: {
    
    {
    
     $attrs }}</p>
    <son
      class="child"
      :son="childname"
      v-bind="$attrs"
      @childMethods="onChildMethods($event)"
      v-on="$listeners"
    ></son>
  </div>
</template>
<script>
import Son from "./gradson.vue";
export default {
    
    
  name: "Child",
  components: {
    
     Son },
  inheritAttrs: true,
  props: {
    
    
    child: {
    
    
      type: String,
      default: () => "",
    },
  },
  data() {
    
    
    return {
    
    
      childname: "child",
    };
  },
  methods: {
    
    
    onChildMethods(e) {
    
    
      console.log(e);
    },
  },
};
</script>

// son
<template>
  <div>
    <h1>son</h1>
    <p style="color: red;">$attrs: {
    
    {
    
     $attrs }}</p>
    <p>
      <button @click="getMethods()">点击触发父作用域中的事件监听器</button>
    </p>
  </div>
</template>
<script>
export default {
    
    
  name: "Son",
  inheritAttrs: false, // 不会继承父组件传递的非props属性
  props: {
    
    
    son: {
    
    
      type: String,
      default: () => "",
    },
  },
  methods: {
    
    
    getMethods() {
    
    
        console.log(this.$attrs); // {haha: 'jjjjjjjjj'}
        this.$emit('parentMethods', 'son 组件触发parent methods---parentMethods')
        this.$emit('childMethods', 'son 组件触发child methods---childMethods')
    }
  }
};
</script>
<style></style>

在这里插入图片描述
在这里插入图片描述

7. eventBus(事件总线)

兄弟组件通讯可以借助VueX来实现,但对于一些简单结构的项目不需要引入VueX造成项目臃肿,或者通过子传父再父传子实现数据的传递。
创建event-bus.js实质上就是导出了一个空的Vue实例,这个实例上挂载了vue的实例方法和事件,外部可以使用$emit/$on发布和订阅事件。
$emit 触发当前实例上的事件。附加参数都会传给监听器回调。
$on 监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

// event-bus.js

import vue from 'Vue'
const eventBus = new Vue() 
export default eventBus

使用

// A.vue
import {
    
     eventBus } from 'event-bus.js'
// 发射事件
eventBus.$emit('methods', params)

// B.vue
import {
    
     eventBus } from 'event-bus.js'
// 接收事件
eventBus.$on('methods', (params) => {
    
    
...
})

或者直接将 eventBus 挂载到vue原型上,注册一个全局的事件总线

// main.js
Vue.prototype.$EventBus = new Vue()

移除监听事件

beforeDestroy () {
    
    
  // 移除该事件所有的监听器
  eventBus.$off('methodsName')
  // 移除所有的事件监听器
  eventBus.$off()
}

猜你喜欢

转载自blog.csdn.net/weixin_45032067/article/details/126551396