Vue 组件通信

本文详细介绍了Vue.js中父子组件间的通信方法,包括props属性从父组件向子组件传递数据,子组件通过$emit向父组件发送事件,以及$refs引用实现直接调用方法,还提到了非父子组件间通信的EventBus和依赖注入provide/inject机制。
摘要由CSDN通过智能技术生成

一、父子组件通信

父组件通过 props 向子组件传递数据,子组件通过 $emit 和父组件通信

1.父组件向子组件传值(props)

props的特点:

  • props只能是父组件向子组件进行传值,props使得父子组件之间形成一个单向的下行绑定。子组件的数据会随着父组件的更新而响应式更新。
  • props可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以是传递一个函数。
  • props属性名规则:若在props中使用驼峰形式,模板中标签需要使用短横线的形式来书写。

用法:

  1. 给父组件中子组件标签,添加属性的方式,传值
  2. 子组件通过props进行接收
  3. 渲染使用

示例
父组件

// 父组件
<template>
    <div id="father">
        <son :msg="msgData" :fn="myFunction"></son>
    </div>
</template>

<script>
import son from "./son.vue";
export default {
    name: father,
    data() {
        msgData: "父组件数据";
    },
    methods: {
        myFunction() {
            console.log("vue");
        }
    },
    components: {
        son
    }
};
</script>

子组件:

// 子组件
<template>
    <div id="son">
        <p>{{msg}}</p>
        <button @click="fn">按钮</button>
    </div>
</template>
<script>
export default {
    name: "son",
    props: ["msg", "fn"]
};
</script>
2.子组件向父组件传递数据($emit)

$emit的特点:

  • $emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数

用法:

  1. 通过$emit,向父组件发送消息通知
  2. 父组件,对消息进行监听
  3. 提供处理函数,提供逻辑

示例:
父组件:

// 父组件
<template>
  <div class="section">
    <Son :articles="articleList" @onEmitIndex="onEmitIndex"></Son>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import Son from './test/Son.vue'
export default {
  name: 'comArticle',
  components: { Son },
  data() {
    return {
      currentIndex: -1,
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>

子组件:

//子组件
<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>

<script>
export default {
  props: ['articles'],
  methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index) // 触发父组件的方法,并传递参数index
    }
  }
}
</script>
3.ref / $refs

介绍:

  • ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向该子组件实例
  • this.$refs是一个对象,持有当前组件中注册过 ref特性的所有 DOM 元素和子组件实例

注意:

  • $refs只有在组件渲染完成后才填充,在初始渲染的时候不能访问它们,并且它是非响应式的,因此不能用它在模板中做数据绑定
  • 当ref和v-for一起使用时,获取到的引用将会是一个数组,包含循环数组源

用法:

 <p ref="p">Hello</p>
<children ref="children"></children>
   this.$refs.p
   this.$refs.children

示例

<template>
<div>
    <div ref="myDiv" v-for="(item, index) in arr" :key="index">{{item}}</div>
</div>
</template>

<script>
export default {
data() {
    return {
    arr: ['one', 'two', 'three', 'four']
    }
},
mounted() {
    console.log(this.$refs.myDiv)
},
methods: {}
}
</script> 
<style lang="sass" scoped>
</style>

ref与$refs实现父子传参
子组件

<template>
    <div>{{msg}}</div>
</template>

<script>
export default {
data() {
    return {
    msg: '我是子组件'
    }
},
methods: {
    changeMsg() {
    this.msg = '变身'
    }
}
}
</script>
<style lang="sass" scoped></style>

父组件

<template>
<div @click="parentMethod">
    <children ref="children"></children>
</div>
</template>

<script>
import children from 'components/children.vue'
export default {
components: { 
    children 
},
data() {
    return {}
},
methods: {
    parentMethod() {
    this.$refs.children  //返回一个对象
    this.$refs.children.changeMsg() // 调用children的changeMsg方法
    }
}
}
</script>
<style lang="sass" scoped></style>
4. eventBus事件总线($ emit / $ on)

eventBus作用
非父子(兄弟)组件之间,进行简易消息传递
在这里插入图片描述

步骤

  1. 创建一个都能访问的事件总线 (空Vue实例
import Vue from 'vue'
// 创建事件总线   就相当于创建了一个新的vue实例
const Bus = new Vue()
// 导出Bus 
export default Bus

  1. A组件(接受方),监听Bus的 $on事件
created () {
  Bus.$on('sendMsg', (msg) => {
    this.msg = msg
  })
}
  1. B组件(发送方),触发Bus的$emit事件
Bus.$emit('sendMsg', '这是一个消息')

注意
有组件发布事件后 剩余的所有组件都可以进行监听事件

示例

eventbus.js

import Vue from 'vue'
// 创建vue实例并导出
const eventbus = new Vue()
export default eventbus

发送方兄弟组件Son.vue

<template>
  <div class="son">
    <button @click="send">传递数据</button>
  </div>
</template>

<script>
import eventbus from '../utils/eventbus'
export default {
  name: 'SonComp',
  data() {
    return {
      msg: '你好熊二',
    }
  },
  methods: {
    send() {
      // 触发事件
      eventbus.$emit('useData', this.msg)
    },
  },
}
</script>

接收方兄弟Son2.vue

<template>
  <div class="son">
    我是熊二-{{ msg }}

  </div>
</template>

<script>
import eventbus from '../utils/eventbus'

export default {
  name: 'Son2Comp',
  data() {
    return {
      msg: '',
    }
  },
  created() {
    // 通过eventbus绑定事件
    eventbus.$on('useData', (v) => {
      this.msg = v
    })
  },
}
</script>

父组件App.vue

<template>
  <div class="wrapper">
    <Son />
    <Son2 />
  </div>
</template>

<script>
import Son from "./components/Son.vue";
import Son2 from "./components/Son2.vue";
export default {
  name: "App",
  provide() {
    return {
      // 提供的数据是引用类型,后代接受到的是响应式
      // 基本类型不是响应式
      foo: this.foo,
      money: this.money,
    };
  },
  data() {
    return {
      foo: [
        {
          id: 1,
          name: "大众",
        },
      ],
      money: 1000,
    };
  },
  components: {
    Son,
    Son2,
  },
  methods: {},
};
</script>
5. 依赖注入 provide&inject

作用 :跨层级共享数据
这种方式就是vue中依赖注入,该方法用于 父子组件之间 的通信。当然这里所说的父子不一定是真正的父子,也可以是祖孙组件,在层数很深的情况下,可以使用这种方式来进行传值。就不用一层一层的传递数据了
provide和inject是vue提供的两个钩子,和data、methods是同级的。并且provide的书写形式和data一样。

  • provide 钩子用来发送数据或方法。
  • inject钩子用来接收数据或方法
    在这里插入图片描述
    语法
  1. 父组件 provide提供数据
export default {
  provide () {
    return {
       // 普通类型【非响应式】
       color: this.color, 
       // 复杂类型【响应式】
       userInfo: this.userInfo, 
    }
  }
}
  1. 子/孙组件 inject获取数据
export default {
  inject: ['color','userInfo'],
  created () {
    console.log(this.color, this.userInfo)
  }
}

注意

  • provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改
6. $parent / $children

作用

  • 使用$parent可以让组件访问父组件的实例(访问的是上一级父组件的属性和方法)。
  • 使用 $children 可以让组件访问子组件的实例,但是, $children 并不能保证顺序,并且访问的数据也不是响应式的。

语法
子组件

<template>
  <div>
    <span>{{message}}</span>
    <p>获取父组件的值为:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Vue'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

父组件中:

<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <child></child>
    <button @click="change">点击改变子组件值</button>
  </div>
</template>

<script>
import child from './child.vue'
export default {
  components: { child },
  data() {
    return {
      msg: 'Welcome'
    }
  },
  methods: {
    change() {
      // 获取到子组件
      this.$children[0].message = 'JavaScript'
    }
  }
}
</script>

在上面的代码中,子组件获取到了父组件的parentVal值,父组件改变了子组件中message的值。

注意:

  • 通过 $parent 访问到的是上一级父组件的实例,可以使用 $root 来访问根组件的实例
  • 在组件中使用$children拿到的是所有的子组件的实例,它是一个数组,并且是无序的
  • 在根组件 #app 上拿 $parent 得到的是 new Vue()的实例,在这实例上再拿 $parent 得到的是undefined,而在最底层的子组件拿 $children 是个空数组
  • $children 的值是数组,而 $parent是个对象
6.$attrs / $listeners

作用

  • $attrs–继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上
  • $ listeners属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 v-on=“$listeners” 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)

语法示例
A组件(App.vue)

<template>
  <div id="app">
    <!-- 此处监听了两个事件,可以在B组件或者C组件中直接触发 -->
    <child1  :pchild1="child1" :pchild2="child2" :pchild3="child3" @method1="onMethod1" @method2="onMethod2"></child1>
  </div>
</template>

<script>
import Child1 from "./Child1.vue";
export default {
  data() {
    return {
      child1:'1',
      child2: 2,
      child3:{
        name:'child3'
      }
    };
  },
  components: { Child1 },
  methods: {
    onMethod1(msg1) {
      console.log(`${msg1} running`);
    },
    onMethod2(msg2) {
      console.log(`${msg2} running`);
    },
  },
};
</script>

B组件(Child.vue)

<template>
  <div class="child-1">
    <h2>in child1</h2>
    <p>props: {{ pchild1 }}</p>
    <p>$attrs: {{ $attrs }}</p>
    <hr/>
    <!-- 通过 v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
    <!-- C组件中能直接触发test的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
    <child2 v-bind="$attrs" v-on="$listeners"></child2>
  </div>
</template>

<script>
import Child2 from "./Child2.vue";
export default {
  data() {
    return {
      child1:'child1'  
    };
  },
  components: { Child2 },
  props: {
    pchild1:{
      type:String
    }
  },  
  inheritAttrs: false,
  mounted() {
    this.$emit("method1",this.child1);
  },
};
</script>

C组件(Child.vue)

<template>
  <div class="child-2">
    <h2>in child2:</h2>
    <p>props: {{ pChild2 }}</p>
    <p>$attrs: {{ $attrs }}</p>
    <p>pchild3Name: {{ $attrs.pchild3.name }}</p>
    <hr/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      child2:'child2'
    };
  },
  props: {
    pChild2:{
      type:String,
    }
  },
  inheritAttrs: false,
  mounted() {
    this.$emit("method2",this.child2);
  },
};
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值