Vue学习(四)组件与组件间的通信

Vue学习——组件与组件间的通信

Day1



前言

此文章根据官方文档及些网络资料编写,仅供个人使用。

提示:以下是本篇文章正文内容,下面案例可供参考

一、组件基本使用

1.1 注册组件的基本步骤

将页面分成若干组件进行开发,然后组件的组成分成三个步骤
第一步调用Vue.extend()方法创建组件
第二步调动Vue.component()方法注册组件
第三步使用组件
  <div id="app">
    <!--3.使用组件-->
    <cpn></cpn>
  </div>
  <script>
    // 1.创建组件构造器对象
    const cpn = Vue.extend({
      template: `
      <div>
        <h2>组件1</h2>
        <p>我是组件1的内容</p>
      </div>`
    })
    // 2.注册组件
    Vue.component('cpn', cpn)
    const app = new Vue({
      el: '#app',
    })
  </script>

结果:在这里插入图片描述

1.2 全局组件

全局组件的意思是在不同的Vue实例下都可以使用
  <div id="app">
    <cpn></cpn>
  </div>
  <div id="app2">
    <cpn></cpn>
  </div>
  <script>
    // 1.创建组件构造器对象
    const cpn = Vue.extend({
      template: `
      <div>
        <h2>组件1</h2>
        <p>我是组件1的内容</p>
      </div>`
    })
    // 2.注册组件
    Vue.component('cpn', cpn)
    const app = new Vue({
      el: '#app',
    })
    const app2 = new Vue({
      el: '#app2',
    })
  </script>

在这里插入图片描述

1.3 局部组件

局部组件只能在注册的实例中使用
  <div id="app">
    <cpn></cpn>
  </div>
  <div id="app2">
    <cpn></cpn>
  </div>
  <script>
    // 1.创建组件构造器对象
    const cpn = Vue.extend({
      template: `
      <div>
        <h2>组件1</h2>
        <p>我是组件1的内容</p>
      </div>`
    })
    const app = new Vue({
      el: '#app',
      // 2.注册组件
      components: {
        'cpn': cpn
      }
    })
    const app2 = new Vue({
      el: '#app2',
    })
  </script>

在这里插入图片描述

1.4 父组件调用子组件

我们在父组件中注册子组件在调用,其实从这里我们就可以看出来,每一个组件都是一个独立的模块,里面有方法,计算属性,等。
  <div id="app">
    <cpn2></cpn2>
  </div>
  <script>
    // 1.创建子组件
    const cpn1 = Vue.extend({
      template: `
      <div>
        <h2>子组件</h2>
        <p>我是子组件的内容</p>
      </div>
    `
    })
    // 2.创建父组件
    const cpn2 = Vue.extend({
      template: `
      <div>
        <h2>父组件</h2>
        <p>我是父组件的内容</p>
        <!-- 在这里调用的是子组件的内容 -->
        <cpn1><cpn1>
      </div>`,
      components: {
        cpn1: cpn1
      }
    })
    const app = new Vue({
      el: '#app',
      components: {
        cpn2: cpn2
      }
    })
  </script>

1.5 组件语法糖

组件语法糖省去了Vue.extend(),使用了一个对象代替
  // 1.注册全局组件语法糖
  Vue.component('cpn1', {
    template: `
      <div>
        <h2>我是全局组件语法糖</h2>
      </div>`
  })
  // 2.注册局部组件的语法糖
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      'cpn2': {
        template: `
          <div>
            <h2>我是局部组件语法糖</h2>
          </div>`
      }
    }
  })

1.6 模板分离

将模板分离到HTML中,其中Vue提供了2种方案
这里要注意script标签的类型必须是text/x-template
使用< script >标签
使用< template >标签
  <div id="app">
    <cpn1></cpn1>
    <cpn2></cpn2>
  </div>
  <!--1.script标签-->
  <script type="text/x-template" id="cpn1">
    <div>
      <h2>我是script</h2>
    </div>
  </script>
  <!--2.template标签-->
  <template id="cpn2">
    <div>
      <h2>我是template</h2>
    </div>
  </template>
  <script>
    // 1.注册script标签组件
    Vue.component('cpn1', {
      template: '#cpn1'
    })
    // 2.注册template标签组件
    Vue.component('cpn2', {
      template: '#cpn2'
    })
    const app = new Vue({
      el: '#app'
    })
  </script>

1.7 组件数据问题

首先组件是一个单独的模块,拥有属于自己的模板,也就是他是不能直接访问Vue实例中的data的。
  <div id="app">
    <cpn></cpn>
  </div>

  <template id="cpn">
    <div>
      <h2>我是Vue实例中的data数据:{{message}}</h2>
      <h2>{{title}}</h2>
      <p>内容</p>
    </div>
  </template>
  <script>
    // 1.注册一个全局组件
    Vue.component('cpn', {
      template: '#cpn',
      data() {
        return {
          title: 'abc'
        }
      }
    })
    const app = new Vue({
      el: '#app',
      data: {
        message: 'data',
      }
    })
  </script>

在这里插入图片描述

我们可以看到结果,message的数据没有显示,而打开控制台也显示找不到message。


在这里插入图片描述

结论:就是组件他有自己存放数据的地方所以data()就出现了

1. 我们继续看代码,你们会想问了,为什么组件中data属性是一个函数。
2. 至于为什么,官方文档的意思是,让每一个组件都返回一个新的对象,如果用同一个对象,那么他们数据会互相影响,意思就是会共享数据。

3. 例如我们设置了2个按钮A和B,每点击一下A按钮,**A按钮+1**,点B按钮则**B按钮+1**
4. 如果是用相同的对象,那么按一下,A和B个按钮**都+1**。

二、组件间通信

在这里插入图片描述

2.1 父组件向子组件传递数据,props

父组件需要通过props向子组件传递数据,props的值有两种实现方式,一种是以**字符串数组**,一种是以**对象**实现。

最简单的实现:
  <div id="app">
  	<!-- 第三步,使用v-bind绑定 -->
    <cpn :chello="hello"></cpn>
  </div>
  <template id="cpn">
  	<!-- 第二步,调用子组件的变量名 -->
    <h2>{{chello}}</h2>
  </template>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        hello: 'Hello'
      },
      components: {
        'cpn': {
          template: '#cpn',
          // 第一步,写上你打算引用的变量名
          props: ['chello']
        }
      }
    })
  </script>

在这里插入图片描述

第一步先在props里面写上你打算引用的变量名,这里我们用chello,然后在模板上调用你子组件刚刚创建的 chello ,然后我们直接调用模板实现cpn模板,使用v-bind将你子组件创建的 chello 与父组件hello相绑定。就结束了


字符串数组:
  <div id="app">
    <cpn :cmessage="message" :chello="hello"></cpn>
  </div>
  <template id="cpn">
    <div>
      <ul>
        <li v-for="item in cmessage">{{item}}</li>
      </ul>
      <h2>{{chello}}</h2>
    </div>
  </template>
  <script>
    // 父传子: props
    const cpn = {
      template: '#cpn',
      props: ['chello', 'cmessage'],
    }
    const app = new Vue({
      el: '#app',
      data: {
        message: ['java', 'python', 'c++'],
        hello: 'Hello'
      },
      components: {
        cpn
      }
    })
  </script>

在这里插入图片描述


对象:
    const cpn = {
      template: '#cpn',
      props: {
        // 1.类型限制
        // cmessage: Array,
        // chello: String,
        // 2.提供一些默认值, 以及必传值
        chello: {
          type: String,
          default: 'aaaaaaaa',
          required: true
        },
        // 类型是对象或者数组时, 默认值必须是一个函数
        cmessage: {
          type: Array,
          default () {
            return []
          }
        }
      },
      data() {
        return {}
      },
    }
对象可以进行类型限制,以及默认值的设置,和是否设置必传值
required: true,意思是这个数值必传的,不传会报错
这里需要注意的是,如果类型是对象或数组的时候,默认值必须是一个函数。

2.2 关于props的驼峰标识问题

我们在使用props传递数据的时候,可能会使用驼峰标识,但是在标签上,Vue他是不支持驼峰标识的。
  <div id="app">
    <cpn :cInfo="info" :childMyMessage="message" />
  </div>
如果你打算这么写的话,运行代码,会显示空对象,你需要在大写前面加上 -
  <div id="app">
    <cpn :c-info="info" :child-my-message="message" />
  </div>

2.3 子组件向父组件传递数据,$emit

我们需要使用自定义事件来完成,具体流程是:
1. 在子组件中使用$emit来触发事件
2. 在父组件中,使用v-on监听事件
  <!--父组件模板-->
  <div id="app">
  	<!--第二步:监听名为item-click的自定义事件-->
    <cpn @item-click="cpnClick"></cpn>
  </div>
  <!--子组件模板-->
  <template id="cpn">
    <div>
      <button v-for="item in laugh" @click="btnClick(item)">
        {{item.name}}
      </button>
    </div>
  </template>
  <script>
    // 1.子组件
    const cpn = {
      template: '#cpn',
      data() {
        return {
          laugh: [{
              id: '1',
              name: 'java'
            },
            {
              id: '2',
              name: 'python3'
            },
            {
              id: '3',
              name: 'html'
            }
          ]
        }
      },
      methods: {
        btnClick(item) {
          // 第一步:发射自定义事件
          this.$emit('item-click', item)
        }
      }
    }

    // 2.父组件
    const app = new Vue({
      el: '#app',
      components: {
        cpn
      },
      methods: {
      	// 第三步:将按钮信息发送到控制台
        cpnClick(item) {
          console.log('cpnClick', item);
        }
      }
    })
  </script>

在这里插入图片描述

当你点击第一个按钮时,控制台会显示该按钮的信息

在这里插入图片描述


2.4 父子组件间双向通信

我们直接做个案例
第一步:让父组件向子组件传递数据
第二步:让子组件向父组件传递数据,同时弄一个输入框可以改变两者的数值
理解:当子组件的值发生变化,父组件也会变化。


第一步很简单,我们就这样写:
  <!--父组件模板-->
  <div id="app">
    <cpn :number1="num1" :number2="num2"></cpn>
  </div>
  <!--子组件模板-->
  <template id="cpn">
    <div>
      <h2>{{number1}}</h2>
      <h2>{{number2}}</h2>
    </div>
  </template>
  <script>
    // 1.子组件
    const cpn = {
      template: '#cpn',
      props: {
        number1: Number,
        number2: Number
      },
    }
    // 2.父组件
    const app = new Vue({
      el: '#app',
      data: {
        num1: 0,
        num2: 1
      },
      components: {
        cpn
      },
    })
  </script>
第二步:
  <!--父组件模板-->
  <div id="app">
    <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
  </div>
  <!--子组件模板-->
  <template id="cpn">
    <div>
      <h2>props:{{number1}}</h2>
      <h2>data:{{dnumber1}}</h2>
      <input type="text" :value="dnumber1" @input="num1Input">
      <h2>props:{{number2}}</h2>
      <h2>data:{{dnumber2}}</h2>
      <input type="text" :value="dnumber2" @input="num2Input">
    </div>
  </template>
  <script>
    // 1.子组件
    const cpn = {
      template: '#cpn',
      props: {
        number1: Number,
        number2: Number
      },
      // 第一步:先定义属于子组件的数值
      data() {
        return {
          dnumber1: this.number1,
          dnumber2: this.number2
        }
      },
      methods: {
        num1Input(event) {
          this.dnumber1 = event.target.value
          this.$emit('num1change', this.dnumber1)
        },
        num2Input(event) {
          this.dnumber2 = event.target.value
          this.$emit('num2change', this.dnumber2)
        }
      },
    }
    // 2.父组件
    const app = new Vue({
      el: '#app',
      data: {
        num1: 0,
        num2: 1
      },
      components: {
        cpn
      },
      methods: {
        num1change(value) {
          this.num1 = parseFloat(value)
        },
        num2change(value) {
          this.num2 = parseFloat(value)
        },
      }
    })
  </script>

在这里插入图片描述

代码解析: 第一步,先创建子组件的元素
      // 第一步:先定义属于子组件的元素
      data() {
        return {
          dnumber1: this.number1,
          dnumber2: this.number2
        }
      },
第二步,先创建个输入框,让输入框的值与子组件元素dnumber1相绑定,然后给输入框绑定个输入事件num1Input
<input type="text" :value="dnumber1" @input="num1Input">
第三步,将输入框的值给到dnumber1子组件元素,然后发送自定义事件:num1change
        num1Input(event) {
          this.dnumber1 = event.target.value
          this.$emit('num1change', this.dnumber1)
        }

第四步,在父组件中使用v-on监听 num1change 自定义事件,触发后调用父组件函数: num1change

  <div id="app">
    <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>

最后一步,父组件将值转型后赋给num1

      methods: {
        num1change(value) {
          this.num1 = parseFloat(value)
        },

2.5 父组件访问子组件

在父组件中访问子组件的数据有两种办法
一种是使用children,将子组件所有对象显示出来
一种是使用refs,先给一个组件绑定一个ID,然后使用 this.$refs.ID 调用

2.5.1 children

  <div id="app">
    <cpn></cpn>
    <cpn ref="test"></cpn>
    <button @click="btnClick">按钮</button>
  </div>
  <template id="cpn">
    <div>我是子组件</div>
  </template>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      },
      methods: {
        btnClick() {
          // 1.$children
          console.log(this.$children);
          /*for (let c of this.$children) {
            console.log(c.name);
            c.showMessage();
          }*/
          // console.log(this.$children[3].name);
        }
      },
      components: {
        cpn: {
          template: '#cpn',
          data() {
            return {
              name: '我是子组件的name'
            }
          },
          methods: {
            showMessage() {
              console.log('showMessage');
            }
          }
        },
      }
    })
  </script>

在这里插入图片描述

2.5.2 refs

        btnClick() {
          // 2.$refs => 对象类型
          console.log(this.$refs.test.name);
        }

在这里插入图片描述

2.6 子组件访问父组件

<div id="app">
  <cpn></cpn>
</div>
<template id="cpn">
  <div>
    <h2>我是cpn组件</h2>
    <ccpn></ccpn>
  </div>
</template>
<template id="ccpn">
  <div>
    <h2>我是子组件</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>
<script>
  const app = new Vue({
    el: '#app',
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是cpn组件的name'
          }
        },
        components: {
          ccpn: {
            template: '#ccpn',
            methods: {
              btnClick() {
                // 1.访问父组件$parent
                // console.log(this.$parent);
                // console.log(this.$parent.name);

                // 2.访问根组件$root
                console.log(this.$root);
                console.log(this.$root.message);
              }
            }
          }
        }
      }
    }
  })
</script>

总结

Vue学习(一)插值操作

Vue学习(二)动态绑定与事件监听

Vue学习(三)循环遍历与表单绑定

Vue学习(四)组件与组件间的通信

Vue学习(五)组件的插槽

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值