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>

 

  

 

posted @ 2019-10-17 13:54  一城柳絮吹成雪  阅读(1389)  评论(0编辑  收藏  举报