Vue 组件通信的多种方式(props、$ref、$emit、$attr、 $listeners)
prop和$ref之间的区别:
prop 着重于数据的传递,它并不能调用子组件里的属性和方法。像创建文章组件时,自定义标题和内容这样的使用场景,最适合使用prop。
$ref 着重于索引,主要用来调用子组件里的属性和方法,其实并不擅长数据传递。而且ref用在dom元素的时候,能使到选择器的作用,这个功能比作为索引更常有用到。
props 父组件传值子组件
父组件传值
//props示例(vue) <template> <div id="app"> <child :message="message"></child> 动态传递
<child message="传递的内容"></child> 静态传递
</div> </template> <script> import Child from "./Child"; export default { components: {Child}, name: "main-page", data() { return { message:{ type:1, name:'Admin', }, a:2, b:4, c:'lalal'+Math.random(), title:'', info:'我是来自父组件', msg:'默认' } }, created() {}, mounted(){}, methods: {} } </script> <style lang="scss" scoped> </style>
子组件接收父组件传值
<template> <div> {{message}} <!--<slot></slot>--> </div> </template> <script> export default { name: "child", props:['message'], data() { return { title:'' } }, mounted() {}, methods:{} } </script> <style lang="scss" scoped> </style>
父组件code引用子组件,通过props可以实现传值。可以传递string , number , object,表达式。对于子组件接受父组件props,可以直接使用props:[‘xxxx’]格式,为了更严谨,可以使用如下方式:
<script> export default { name: "child", // props:['message'], props:{ message:{ type:Object, default:function () { return {} } } }, data() { return { title:'' } }, mounted() { // this.$emit('showMessage','我是来自子组件') }, methods:{ } } </script>
$emit 子组件向父组件传值
props、ref 实现的都是父组件=>子组件之间的通信,而$emit则可实现子组件向父组件的通信, $emit应用的范围较为广泛。
子组件传值给父组件
template> <div> <input ref="myBtn"></input> </div> </template> <script> export default { name: "index", data() { return { info:'ref可以获取到元素哦~' } }, mounted() { this.$emit('showMessage','我是来自子组件') }, methods:{ } } </script>
父组件接收子组件传值
<template> <div id="app"> <child @showMessage="showMessage"></child> </div> </template> <script> import Child from "./Child"; export default { components: { Child}, name: "main-page", data() { return { message:{ type:1, name:'Admin', }, fromchildinfo :'我是来自父组件', msg:'默认' } }, created() { }, mounted(){ }, methods: { showMessage (data) { this.fromchildinfo = data }, } } </script>
$ref 的使用
说明:vm.$refs 一个对象,持有已注册过 ref 的所有子组件(或HTML元素)
使用:在 HTML元素 中,添加ref属性,然后在JS中通过vm.$refs.属性来获取
注意:如果获取的是一个子组件,那么通过ref就能获取到子组件中的data和methods
添加ref属性
<div id="app"> <h1 ref="h1Ele">这是H1</h1> <hello ref="ho"></hello> <button @click="getref">获取H1元素</button>
</div>
获取注册过 ref 的所有组件或元素 methods: { getref() { // 表示从 $refs对象 中, 获取 ref 属性值为: h1ele DOM元素或组件 console.log(this.$refs.h1Ele.innerText); this.$refs.h1ele.style.color = 'red';// 修改html样式 console.log(this.$refs.ho.msg);// 获取组件数据 console.log(this.$refs.ho.test);// 获取组件的方法 } }
<input ref="count" type="text" v-model="active.name" required name="name" value="">
这样在vue中我们可以使用$ref来获取dom节点,进行一些dom的操作
下面示例:控制input输入框的文字个数
new Vue({
el:'#app',
data:{
active:{'name':''}
},
watch:{
active:{
handler:function(){
var _this = this;
var _sum = 4; //字数限制为4个
_this.$refs.count.setAttribute("maxlength",_sum);
},
deep:true
}
},
})
使用在子组件上,可以用来获取子组件的属性值,假设子组件里面都有一个属性news
<!-- 父组件 -->
<div id="app">
<hdnews ref="hdnews"></hdnews>
<hdinfo ref="hdinfo"></hdinfo>
</div>
new Vue({
el:'#app',
mounted () {
console.log(this.$refs.hdnews.news); //获取子组件的值
console.log(this.$refs.hdinfo.news);
this.$refs.msg.getMessage('我是子组件一!') //调用子组件的方法
}
})
<!-- 子组件 -->
<template>
<h3>{{message}}</h3>
</template>
<script>
export default {
data(){
return{
news:'我是子组件的数据'
}
},
methods:{
getMessage(m){
this.message=m;
}
}
}
</script>
$attr、 $listeners
场景提出:A、B、C三个组件,需要实现A=>B=>C,进行传递(结构较为简单,无需使用vuex)。当然实现方式也可以$emit,一层一层传递下去,但这样代码显得冗余。在vue2.4之后,提出 $attr、 $listeners ,可以实现快速传递。
组件A code:
<template> <div id="app"> <son :info="info" @getData="getData"></son> <div>{{msg}}</div> </div> </template> <script> import Son from "./son"; export default { components: { Son, Test, Child}, name: "main-page", data() { return { message:{ type:1, name:'Admin', }, a:2, b:4, c:'lalal'+Math.random(), title:'', info:'我是来自父组件', msg:'默认' } }, created() { }, mounted(){ }, methods: { getData (val) { this.msg = val }, } } </script> <style lang="scss" scoped> #app { width: 375px; height: 100%; } </style>
b组件
<template> <temp-son v-bind="$attrs" v-on="$listeners"></temp-son> </template> <script> import TempSon from "./tempSon"; export default { components: {TempSon}, name: "son", props:[] } </script> <style scoped> </style>
c组件
<template> <div> <h1 class="btn">{{this.$attrs.info}}</h1> </div> </template> <script> export default { name: "temp-son", mounted() { this.$emit('getData','我来自孙子组件') } } </script> <style scoped> </style>
$attr、 $listeners 的TypeScript写法
A组件
<template> <div class="home"> <img alt="Vue logo" src="@/assets/images/logo.png" /> <HelloWorld :info="info" @sunchangedata="getData" :msg="info" /> </div> </template> <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src @Component({ components: { HelloWorld } }) export default class Home extends Vue { info = "你不是好人?"; getData(val:any){ this.info = val; }; } </script>
b组件
<template> <div class="hello"> <h1>{{ msg }}</h1> <p>hello hello 我是来自子组件</p> <Sun v-bind="$attrs" v-on="$listeners"></Sun> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator"; import Sun from "./sun.vue"; // @ is an alias to /src @Component({ components:{ Sun } }) export default class HelloWorld extends Vue { inheritAttrs = false; @Prop() private msg!: string; } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> </style>
c组件
<template> <div> <p>sun组件</p> <p>来自爷爷辈:{{ $attrs.info }}</p> <button @click="$emit('sunchangedata','孙子说爷爷是好人')">点击更改爷爷辈信息</button> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator"; export default class Sun extends Vue { } </script> <style> </style>