Vue——06组件化之——子传父($emit)、监听原生点击事件、父子组件通信、实现父子组件的双向绑定、ref、$refs、is、:is的使用以及区别

简介: 子传父($emit)、监听原生点击事件、父子组件通信、实现父子组件的双向绑定、ref、$refs、is、:is的使用以及区别

上一篇说了一下父传子,详情参考:父组件给子组件传递数据——props属性

父传子既然有父传子那么肯定有子传父,有子传父肯定也有两者之间相互绑定

这里我们先看一下子传父的写法:

一、子传父:$emit()

看代码:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title>子传父</title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <style type="text/css">
  button {
    margin-left: 5px;
  }
  </style>
  <body>
  <div id="app">
    <cpn1 @itemclick="cpnclick"></cpn1>
  </div>
  <template id="cpn1">
    <div>
    <button type="button" v-for="item in menu" :key="item.id"
      @click="btnclick(item)">{{item.name}}</button>
    </div>
  </template>
  <script type="text/javascript">
    const cpn = {
    template: "#cpn1",
    data() {
      return {
      menu: [{
        id: 'one',
        name: '首页'
        },
        {
        id: 'two',
        name: '分类'
        },
        {
        id: 'three',
        name: '购物'
        },
        {
        id: 'four',
        name: '我的'
        },
      ],
      }
    },
    methods: {
      btnclick(item) {
      this.$emit('itemclick', item)
// 子传父 在子组件中做一个点击事件通过$emit派发出 给父组件 同时可以携带参数
      }
    }
    };
    const vm = new Vue({
    el: '#app',
    methods: {
      cpnclick(item) {
      console.log('cpnclick' + item.name);
      }
    },
    components: {
      "cpn1": cpn
    }
    })
  </script>
  </body>
</html>

打印效果:

1d3c61fa7d0b403fb70d63192e10ae65.png

两者之间的关系:

1、父组件可以使用 props 把数据传给子组件。

2、子组件可以使用 $emit 触发父组件的自定义事件。

二、监听原生点击事件:.native

不加.native时,不会触发原生的点击事件

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title>不加native修饰符</title>
  </head>
  <style>
  div{
    cursor: pointer;
  }
  </style>
  <body>
  <div id="app">
          <cpn @click="handelClick"></cpn>    //    这里没有加native修饰符
  </div>
  <!-- 子组件 -->
    <template id="cpn">
      <div>
        我是子组件
      </div>
    </template>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const cpn = {
    template:'#cpn',
    }
    const app = new Vue({
    el: "#app",
    methods: {
                  handelClick(){
       console.log('click');
      }
    },
    components:{
      cpn
    }
    })
  </script>
  </body>
</html>

效果如下:

1b9e320c3a7f425399cec7e7fa2046bb.png

不加修饰符是不会监听到原生点击事件的。

如果是加了.native修饰符时:

添加方法:

<cpn @click.native="handelClick"></cpn>

效果如下图所示:

584e46042bf9401cb0900f1fd710ea90.png

三、组件通信的案例:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <cpn :number1="num1" :number2="num2"></cpn>
  </div>
  <template id="cpn">
    <div>
    <h2>{{datanum1}}</h2>
    <input type="text" v-model="datanum1"/>
    <h2>{{datanum2}}</h2>
    <input type="text" v-model="datanum2"/>
    </div>
  </template>  
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const cpn = {
    template:'#cpn',
    data(){
      return {
      datanum1:this.number1,
      datanum2:this.number2,
      }
    },
    props:{
      number1:{
      type:[String,Number]
      },
      number2:{
      type:[String,Number]
      },
    }
    }
    const app = new Vue({
    el: "#app",
    data() {
      return {
      num1:1,
      num2:2
      }
    },
    components:{
      cpn
    }
    })
  </script>
  </body>
</html>

效果如下:

4692efe182214074a1b0bd8553e7d470.png

四、实现父子之间的值的双向绑定

在子组件中添加监听器,利用props和$emit来进行父子之间的双向绑定

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
    <cpn :number1="num1" :number2="num2" @dataclick1="changeClick1" @dataclick2="changeClick2"></cpn>
  </div>
  <template id="cpn">
    <div>
    <h2>{{datanum1}}</h2>
    <h3>number1:{{number1}}</h3>
    <input type="text" v-model="datanum1" />
    <h2>{{datanum2}}</h2>
    <h3>number2:{{number2}}</h3>
    <input type="text" v-model="datanum2" />
    </div>
  </template>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const cpn = {
    template: '#cpn',
    data() {
      return {
      datanum1: this.number1,
      datanum2: this.number2
      }
    },
    props: {
      number1: {
      type: [String, Number]
      },
      number2: {
      type: [String, Number]
      }
    },
    watch: {
      datanum1(n) {
      console.log('datanum1被监听了');
      this.$emit('dataclick1', n / 100)
      },
      datanum2(n) {
      console.log('datanum2被监听了');
      this.$emit('dataclick2', n * 100)
      }
    }
    }
    const app = new Vue({
    el: "#app",
    data() {
      return {
      num1: 1,
      num2: 2
      }
    },
    methods: {
      changeClick1(value) {
      this.num1 = value
      },
      changeClick2(value) {
      this.num2 = value
      }
    },
    computed: {
    },
    components:{
      cpn
    }
    })
  </script>
  </body>
</html>

效果

68c51d1bc78a4b7b8b4bade40a5b38dc.gif

一个是除10一个是

五、父访问子 $refs

JavaScript中获取元素可以使用document.querySelector,那可以在Vue中使用吗?

我们可以测试一下

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
  <div id="app">
    <p id="bb" @click="handelClick" ref="aa">Nanchen</p>
  </div>
  <script type="text/javascript">
    const vm = new Vue({
    el: '#app',
    data() {
      return {
      }
    },
    methods: {
      handelClick(){
      var bb = document.querySelector('bb');
      console.log(bb);
      }  
    }
    })
  </script>
  </body>
</html>

打印结果:

8c7d6658838040b3bc712c48231dc1a4.png

答案是可以的,但是如果使用原生JS获取元素的话,那么用Vue就没有意义了,Vue中有特定的语法

官网解释:

$refs方式:ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
  <div id="app">
    <p id="bb" @click="handelClick" ref="aa">Nanchen</p>
  </div>
  <script type="text/javascript">
    const vm = new Vue({
    el: '#app',
    data() {
      return {
      }
    },
    methods: {
      /* handelClick(){
      var bb = document.querySelector('bb');
      console.log(bb);
      } */
      handelClick() {
      console.log(this.$refs.aa);
      }
    }
    })
  </script>
  </body>
</html>

效果与上图一致:

d99c26ede28c4efba4928ec74a243b1c.png

六、使用$refs获取组件中的值

既然可以获取普通元素那么也可以获得组件中的元素或者值

看这个例子:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <cpn ref="aaa"></cpn>
    <button @click="handelClick">点击</button>
  </div>
  <!-- 子组件 -->
    <template id="cpn">
      <div>
        我是子组件
      </div>
    </template>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const cpn = {
    template:'#cpn',
    data(){
      return {
      name:'我是子组件的name'    //获取子组件的属性
      }
    },
    }
    const app = new Vue({
    el: "#app",
    methods: {
                  handelClick(){
       console.log(this.$refs.aaa.name);
      }
    },
    components:{
      cpn
    }
    })
  </script>
  </body>
</html>

效果如下:

0ed30f7b286f454baca2f860d550f2bb.png

下面看一个扩展:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
  <div id="app">
    <count ref="one" @change="handclick"></count>
    <count ref="two" @change="handclick"></count>
    <h2>{{total}}</h2>
  </div>
  <script type="text/javascript">
  Vue.component('count',{
    template:
    `<div @click="handclick">
    {{number}}
    </div>`,
    data(){
    return{
      number:0
    }
    },
    methods:{
    handclick(){
      this.number++;
      this.$emit('change')
    }
    }
  })
    const vm = new Vue({
    el:'#app',
    data(){
      return{
      total:0
      }
    },
    methods:{
      handclick(){
      this.total= this.$refs.one.number + this.$refs.two.number
      }
    }
    })
  </script>
  </body>
</html>

效果如下:

854895e8bef54e83a4f97e290a51c72d.png

37bfb6c0759747788f16e43c73924ff7.png

不仅如此,ref还可以调用组件中的方法:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <hello-world ref="hello"></hello-world>
     <button @click="getHello">获取helloworld组件中的值</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    Vue.component('helloWorld',{
    template:`<div>helloWorld</div>`,
    data(){
      return {
      number:0
      }
    },
    methods:{
      /*handelClick(){
      console.log('我是子组件的方法');
      }*/
    }
    })
    const app = new Vue({
    el: "#app",
    data: {
    },
    methods: {
                 getHello(){
      /* this.$refs.hello.handelClick(); */
      console.log(this.$refs.hello.$el.innerHTML);
     }
    },
    })
  </script>
  </body>
</html>

效果如下:

0571eb15dfaa4c79b072ef5ce1837670.png

七、is与:is

is

作用:解决了html模板的限制。

看下面这段代码:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
  <div id="app">
    <table>
    <row></row>
    </table>
  </div>
  <script type="text/javascript">
  Vue.component('row',{
    template: '<tr><td>111</td></tr>'
  })
    const vm = new Vue({
    el:'#app',
    data(){
      return{
      }
    },
    })
  </script>
  </body>
</html>

会正常输出

f13bb55ae4a147c7a85af5199d058ed5.png

但是:

79cc9e7cfc534bc0a271b023a16ace65.png

会发现tr并不在table中,

解决办法:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <table>
    <tr is="row"></tr>
    </table> 
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    Vue.component('row', {
    template: '<tr><td>111</td></tr>'
    })
    const app = new Vue({
    el: "#app",
    })
  </script>
  </body>
</html>

打印结果:

93d49f0cd79c4ab4909237014b2192a8.png

用:is还可以用来绑定动态组件

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
  <div id="app">
    <component :is="type"></component>
    <button type="button" @click="changeClick">切换</button>
  </div>
  <script type="text/javascript">
  // 这里要定义两个全局组件
    Vue.component('child-one',{
    template:'<div>child-one</div>'
    }),
    Vue.component('child-two',{
    template:'<div>child-two</div>'
    })
    const vm = new Vue({
    el:'#app',
    data(){
      return{
      type:'child-one'
      }
    },
    methods:{
      changeClick(){
        this.type = this.type === 'child-one' ? 'child-two' :'child-one'
      }
    }
    })
  </script>
  </body>
</html>

效果如下:

4cb24eb32ccc4527abc56f130ddab486.png

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
17天前
|
JavaScript
关于Vue非父子组件通信遇到的细节问题
本文讨论了Vue中非父子组件通信的一个细节问题,即当使用事件总线($eventsBus)进行通信时,需要确保两个组件能够同时在页面上显示,否则可能无法正确触发和监听事件。作者通过组件A和B的例子说明了这一点,并指出解决方案是在一个共同的父组件C中监听事件。
15 1
关于Vue非父子组件通信遇到的细节问题
|
15天前
|
JavaScript
在 Vue 3 组件通信方式中,Provide / Inject 与 Vuex 的区别是什么?
在 Vue 3 组件通信方式中,Provide / Inject 与 Vuex 的区别是什么?
117 65
|
15天前
|
JavaScript 前端开发 API
Vue2与Vue3插槽使用的区别及案例
Vue 3在插槽功能上的改进,体现了其对开发体验的持续优化。通过简化API、加强动态特性和提升性能,Vue 3使得插槽的使用更加灵活和高效。这些改进不仅有助于减轻开发者的负担,还为组件之间的高级交互和内容复用打开了新的可能性。随着Vue生态系统的不断成熟,我们有理由相信,Vue将继续为前端开发提供强大且易用的工具。
20 3
|
17天前
|
前端开发 JavaScript 开发者
React 和 Vue.js 框架的区别是什么?
React 和 Vue.js 框架的区别是什么?
|
17天前
Vue3基础(十san)___父子组件通信___props、emit___provide、inject
文章介绍了Vue3中父子组件通信的两种方式:通过`props`和`emit`传递数据和事件,以及使用`provide`和`inject`在组件树中传递数据。
35 0
Vue3基础(十san)___父子组件通信___props、emit___provide、inject
|
27天前
|
JavaScript 索引
Vue 2和Vue 3的区别以及实现原理
Vue 2 的响应式系统通过Object.defineProperty来实现,它为对象的每个属性添加 getter 和 setter,以便追踪依赖并响应数据变化。
31 9
|
17天前
|
JavaScript
Vue3基础(23)___vue3非父子组件之间的通信
本文介绍了Vue 3中非父子组件间通信的方法,通过创建一个事件集合对象和相应的触发监听函数,实现跨组件的事件传递和监听。
17 0
|
1月前
|
JavaScript 前端开发 编译器
对Vue2 与 Vue3 的区别的理解
【9月更文挑战第3天】对Vue2 与 Vue3 的区别的理解
57 0
|
1天前
|
JSON JavaScript 前端开发
vue尚品汇商城项目-day00【项目介绍:此项目是基于vue2的前台电商项目和后台管理系统】
vue尚品汇商城项目-day00【项目介绍:此项目是基于vue2的前台电商项目和后台管理系统】
7 1
|
2天前
|
JavaScript 前端开发
vue尚品汇商城项目-day01【4.完成非路由组件Header与Footer业务】
vue尚品汇商城项目-day01【4.完成非路由组件Header与Footer业务】
10 2