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

相关文章
|
9月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
777 2
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
1223 0
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
1502 4
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
729 161
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
1278 159
|
JavaScript 前端开发 UED
Vue 表情包输入组件的实现代码:支持自定义表情库、快捷键发送和输入框联动的聊天表情解决方案
本文详细介绍了在 Vue 项目中实现一个功能完善、交互友好的表情包输入组件的方法,并提供了具体的应用实例。组件设计包含表情分类展示、响应式布局、与输入框的交互及样式定制等功能。通过核心技术实现,如将表情插入输入框光标位置和点击外部关闭选择器,确保用户体验流畅。同时探讨了性能优化策略,如懒加载和虚拟滚动,以及扩展性方案,如自定义主题和国际化支持。最终,展示了如何在聊天界面中集成该组件,为用户提供丰富的表情输入体验。
921 8
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
832 1
|
存储 JavaScript 前端开发
基于 ant-design-vue 和 Vue 3 封装的功能强大的表格组件
VTable 是一个基于 ant-design-vue 和 Vue 3 的多功能表格组件,支持列自定义、排序、本地化存储、行选择等功能。它继承了 Ant-Design-Vue Table 的所有特性并加以扩展,提供开箱即用的高性能体验。示例包括基础表格、可选择表格和自定义列渲染等。
1179 6