vue的组件化+父子组件的通信

1.注册组件的基本步骤
1.1 原始的创建方法

组件的使用分为个步骤:

  • 创建组件构造器
  • 注册组件
  • 使用组件

在这里插入图片描述

下面按上面步骤实现简易组件:

<body>
    <div class="container">
      <!-- 使用组件 my-comp -->
      <my-comp></my-comp>
    </div>
    <script src="./vue.js"></script>
    <script>
      // 1.创建组件构造器
      const comp = Vue.extend({
        template: `<div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1689507601,2788277149&fm=26&gp=0.jpg"
        />
      </div>`,
      });
      // 2.注册组件
      Vue.component("my-comp", comp);

      var app = new Vue({
        el: ".container",
        data: {},
        methods: {},
      });
    </script>
  </body>

组件能正常使用:
在这里插入图片描述

讲解:

  • Vue.extend()中参数是一个对象,里面有很多属性。这里只使用了template属性,定义了组件的模板,这就实现了创建组件构造器。
  • Vue.component(参数1,参数2):参数1定义的是使用组件的标签名,参数2传入的是第一步创建的组件构造器。
  • 直接使用第二部的参数1使用组件:例如< my-comp ></ my-comp >。

注意:组件一定要在vue实例中使用才有效,上面的container容器就是一个vue实例。

1.2语法糖创建方法

有了语法糖,我们这里就不需要使用组件构造器函数了。

<body>
    <div class="container1">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>

    <script src="./vue.js"></script>
    <script>
      Vue.component("my-comp", {
        template: `<div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
        />
      </div>`,
      });
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
      });
    </script>
  </body>

注意:直接使用Vue.component()来实现了组件创建,在Vue.component()内部调用了Vue.extend()。这是vue帮我们实现了。

在实际开发中,我们不是在Vue.component()的第二个参数来装载模板的,一个组件的元素并不像我们上面的那么简单,如果都写在参数中,就显得js代码特别臃肿。

开发中这样写:

 <body>
    <template id="component">
      <div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
        />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>

    <script src="./vue.js"></script>
    <script>
      Vue.component("my-comp", {
        template: component
      });
      // vue实例1
      var app1 = new Vue({
        el: ".container1"
      });
    </script>
  </body>

在这里插入图片描述

单独利用template标签来装载模板内容,然后设置id,在Vue.component()中使用这个id来创建组件。

以上就实现了简易的组件构建与使用。

2.全局组件与局部组件

全局组件与局部组件如何区分?

1.当组件在vue实例中注册时,则是局部组件,只能在该vue实例中使用。
2.当组件不是在实例中注册时,则是全局组件,在任意的vue实例都可以使用。

2.1 全局组件

我们在上面定义的my-comp组件就是全局组件,他可以在任意vue实例使用:

 <body>
    <div class="container1">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>
    <div class="container2">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 1.创建组件构造器
      const comp = Vue.extend({
        template: `<div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
        />
      </div>`,
      });
      // 2.注册组件
      Vue.component("my-comp", comp);

      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        data: {},
        methods: {},
      });

      // vue实例2
      var app2 = new Vue({
        el: ".container2",
      });
    </script>
  </body>

上面定义了app1、app2两个vue实例,都使用了my-comp组件:
在这里插入图片描述

2.2 局部组件

在vue实例中注册组件:

 <body>
    <div class="container1">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>
    <div class="container2">
      <!-- 使用组件 -->
      <my-comp></my-comp>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 1.创建组件构造器
      const comp = Vue.extend({
        template: `<div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
        />
      </div>`,
      });

      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        //  注册组件
        components: {
          "my-comp": comp,
        },
      });

      // vue实例2
      var app2 = new Vue({
        el: ".container2",
      });
    </script>
  </body>

上面我只在app1中注册了组件:
在这里插入图片描述
很明显,app2实例没有注册组件,所以无法使用该组件,这就是局部组件。

2.3语法糖局部组件(最简写法)

在vue实例中注册组件使用的是components属性,key是组件标签名,value是组件构造器。

 <body>
    <template id="component">
      <div>
        <p>哈哈哈</p>
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        data: {},
        components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
          },
        },
      });
    </script>
  </body>

在这里插入图片描述

3. 父子组件

简单来说:父子组件是嵌套关系,子组件被包含在父组件中。子组件在父组件中“注册”。

<body>
    <template id="father">
      <div>
        <p>
          这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
        </p>
        <img
          src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
        />
        <my-son></my-son>
      </div>
    </template>
    <template id="son">
      <div>
        <p>
          爱你哟
        </p>
        <img
          src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2346251443,2005527457&fm=26&gp=0.jpg"
        /></div
    ></template>
    <div class="container1">
      <!-- 使用组件 -->
      <my-father></my-father>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 1.子组件构造器
      const son = Vue.extend({
        template: "#son",
      });

      // 2.父组件构造器
      const father = Vue.extend({
        template: "#father",
        // 注意:在父组件中注册子组件
        components: {
          "my-son": son,
        },
      });
      //  3.父组件注册
      Vue.component("my-father", father);

      // vue实例1
      var app1 = new Vue({
        el: ".container1",
      });
    </script>
  </body>

在这里插入图片描述
注意

  • 子组件的“注册”是发生在父组件的构造器中利用components属性注册的。
  • 在父组件的template模板中引用子组件的标签。
  • 子组件是局部组件,只能在父组件中被引用。

特别注意:vue实例也是一个组件,而且是根组件,因此father组件也是根组件的子组件。

4.组件中的动态数据

什么是动态数据?

上面我们的数据是直接写死的,我们都知道标签元素中要想内容是动态的就使用胡子语法,属性想要是动态的就是用v-bind,这里我们重点关注元素标签中的数据是在何处定义以及如何定义。

<body>
    <template id="component">
      <div>
        <p>{{content}}</p>
        <img v-bind:src="imgSrc" />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      Vue.component("my-component", {
        template: "#component",
        data: function () {
          return {
            content:
              "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
            imgSrc:
              "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
          };
        },
      });

      // vue实例1
      var app1 = new Vue({
        el: ".container1",
      });
    </script>
  </body>

正常显示:
在这里插入图片描述
注意这段代码:

 Vue.component("my-component", {
        template: "#component",
        data: function () {
          return {
            content:
              "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
            imgSrc:
              "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
          };
        },
      });

看到我们的data属性了吗?

特别注意:

  • 我们组件中要使用的数据不是在vue实例的data属性中定义的,而是在Vue.component()中利用data属性定义的。
  • 组件中data属性是一个函数,且返回一个对象,对象中包含组件想要的数据,而vue实例的data属性直接就是一个对象。

a.为什么组件的data属性必须是一个函数,再通过函数返回一个对象?

倘若我们data属性就是一个对象,会有“数据共享”的问题。
一个组件的封装可以使我们在很多地方复用组件,那么我们复用组件时,组件显示的内容往往是不一样的,假如data属性是一个对象,那么我们在复用组件时,都是显示同一组数据,从而无法实现显示不同内容的效果。

为什么会数据共享呢?

这得根据我们的内存来谈,对象是“栈堆存储”
在这里插入图片描述
即存储对象时,存储了变量名和一个地址,当我们调用变量时,是通过地址去找到相应的数据,同一个对象只有一个地址。因此,假如data属性是一个对象,则只要调用该对象,都是指向那个地址的同一数据,所以每个组件的data都是相同的。不符合我们实际。

举例说明:(非函数返回的对象)

 const obj = {
        name: "大白",
        age: 20,
      };
      let a = obj;
      let b = obj;
      let c = obj;
      console.log("修改前:", a.name, b.name, c.name);
      a.name = "小白";
      console.log("修改后:", a.name, b.name, c.name);

在这里插入图片描述
组件中的data属性如果不是通过函数返回对象,就会像上面的obj对象一样,无论复用多少组件,数据都一样,而且修改一个实例,其它实例的数据也同样被改变。这就是为什么组件中的data属性是一个函数返回的对象的原因。

b.为什么使用函数返回一个对象就不会数据共享呢?

我们应该知道函数是有自己独立的作用域的,每个函数实例都有一个独立的作用域。

看实例:

function test() {
        return {
          name: "大白",
          age: 22,
        };
      }

      let a = test();
      let b = test();
      let c = test();
      console.log("修改前:", a.name, b.name, c.name);
      a.name = "小白";
      console.log("修改后:", a.name, b.name, c.name);

a、b、c分别是test函数返回的对象,它们都有自己的独立作用域,所以在修改对象a时,它是不会影响b、c对象的属性值的。
在这里插入图片描述

5. 父子组件传值

在实际开发中,往往一些数据需要从上层传递到下层:

  • 比如在一个页面中,我们从服务器中请求到很多数据
  • 其中一部分数据,并非都是整个页面的大组件来显示的,而是需要里面的子组件进行显示
  • 这时,我们并不会在子组件再发送一次网络请求,而是让父组件将数据传递给子组件。

如何进行父子组件间的通信呢?

  • 通过props向子组件传递数据
  • 通过事件向父组件发送消息

在这里插入图片描述

5.1 父传子

注意:vue实例是一个根组件,这里使用根组件与一个自定义组件形成父子组件。

props基本用法:

  • 在组件中,使用选项props来声明需要从父级接收到的数据。
  • props的值有两种方式:
    1. 字符串数组,数组中的字符串就是传递时在子组件使用数据的名称。
    2. 对象,对象可以设置传递时的类型,也可以设置默认值等。

a.字符串数组格式:

<body>
    <template id="component">
      <div>
        <p>{{content1}}</p>
        <img v-bind:src="img1" />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component
        v-bind:content1="content"
        v-bind:img1="imgSrc"
      ></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        data: {
          content:
            "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
          imgSrc:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
        },
        components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
            props: ["content1", "img1"],
          },
        },
      });
    </script>
  </body>

使用思路:

  • 在父组件的data属性中定义传给子组件的数据
data: {
          content:
            "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
          imgSrc:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
        },
  • 注册子组件 中使用props属性使用数组 来定义在子组件使用数据的变量名
 components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
            props: ["content1", "img1"],
          },
  • 使用子组件标签中,使用动态绑定属性格式来传递数据,属性名是props属性数组中的变量名,父组件中的变量名
<my-component
        v-bind:content1="content"
        v-bind:img1="imgSrc"
></my-component>
  • 在组件模板中使用动态绑定属性使用关于属性类型的数据,而标签内容则使用胡子语法
<template id="component">
      <div>
        <p>{{content1}}</p>
        <img v-bind:src="img1" />
      </div>
</template>

b.对象格式:

看代码:

   <body>
    <template id="component">
      <div>
        <p>{{content1}}</p>
        <img v-bind:src="img1" />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component
        v-bind:content1="content"
        v-bind:img1="imgSrc"
      ></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        data: {
          content:
            "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
          imgSrc:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
        },
        components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
            props: {
              content1: {
                type: String,
                default: "我是默认值",
              },
              img1: {
                type: String,
                required: true,//必须,即务必给组件标签传值,否则报错
              },
            },
          },
        },
      });
    </script>
  </body>

在这里插入图片描述
对象形式可设置的属性更多:

  • default:设置默认值
  • required:该数据是否是必须传给子组件

实例:

  <body>
    <template id="component">
      <div>
        <p>{{content1}}</p>
        <img v-bind:src="img1" />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component v-bind:content1="content"></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        data: {
          content:
            "这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
          imgSrc:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
        },
        components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
            props: {
              content1: {
                type: String,
                default: "我是默认值",
              },
              img1: {
                type: String,
                default:
                  "https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3743194253,688329487&fm=26&gp=0.jpg",
                required: true, //
              },
            },
          },
        },
      });
    </script>
  </body>

在这里插入图片描述
认真看代码:

<my-component v-bind:content1="content"></my-component>

我们并没有给子组件传递img的属性值,它使用的是默认属性值,所以显示的是小黄熊,而且也在浏览器中报错。

5.2 子传父($emit)

实例:我们从子组件传给父组件,用户鼠标所点击图片对应的id号

<body>
    <template id="component">
      <div>
        <img
          v-for="item in imgData"
          v-bind:src="item.src"
          v-on:click="returnData(item.id)"
        />
      </div>
    </template>

    <div class="container1">
      <!-- 使用组件 -->
      <my-component v-on:son="getData"></my-component>
    </div>

    <script src="./vue.js"></script>
    <script>
      // vue实例1
      var app1 = new Vue({
        el: ".container1",
        methods: {
          getData(id) {
            console.log(`点击了id==${id}的图片`);
          },
        },
        components: {
          // 在vue实例注册组件,形成父子关系,局部组件
          "my-component": {
            template: "#component",
            data: function () {
              return {
                imgData: [
                  {
                    id: 1,
                    src:
                      "https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=121777500,749621780&fm=26&gp=0.jpg",
                  },
                  {
                    id: 2,
                    src:
                      "https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=272736508,2842806735&fm=26&gp=0.jpg",
                  },
                  {
                    id: 3,
                    src:
                      "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2064238529,2898748334&fm=26&gp=0.jpg",
                  },
                ],
              };
            },
            methods: {
              returnData(id) {
                //发射事件
                this.$emit("son", id);
              },
            },
          },
        },
      });
    </script>
  </body>

在这里插入图片描述
在上面动图中,我分别点击了第三、第二、第一的图片,查看右边的检查,打印出id,已经正常获得子组件传过来的id。

这个例子中我直接在子组件定义数据,而不是父组件传给子组件数据:

data: function () {
              return {
                imgData: [
                  {
                    id: 1,
                    src:
                      "https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=121777500,749621780&fm=26&gp=0.jpg",
                  },
                  {
                    id: 2,
                    src:
                      "https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=272736508,2842806735&fm=26&gp=0.jpg",
                  },
                  {
                    id: 3,
                    src:
                      "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2064238529,2898748334&fm=26&gp=0.jpg",
                  },
                ],
              };
            },

上面我们已经讲过,组件自定义数据是通过data属性定义一个函数,然后返回一个对象。

子传父思路:

  • 在子组件模板中定义好鼠标点击触发事件(或其它触发事件):(v-on:click=“returnData(item.id)”)
     <template id="component">
      <div>
        <img
          v-for="item in imgData"
          v-bind:src="item.src"
          v-on:click="returnData(item.id)"
        />
      </div>
    </template>
  • 由于我们在子组件中触发函数,所以returnData()(自定义函数名)函数在子组件定义:
methods: {
              returnData(id) {
                //发射事件
                this.$emit("son", id);
              },
            },
  • 当鼠标点击触发上面函数,函数内部通过$emit来向父组件发射一个自定义函数和要发送的数据
  • 既然子组件发射函数,那么我们就需要监听接收它发射的函数,在子组件标签接收函数:(v-on:son)son是发射器自定义发射的变量名
    <div class="container1">
      <!-- 使用组件 -->
      <my-component v-on:son="getData"></my-component>
    </div>
  • getData()是在父组件自定义的处理来自子组件数据的函数:
 methods: {
          getData(id) {
            console.log(`点击了id==${id}的图片`);
          },
        },

注意:v-on:son="getData"的getData函数不能加括号以及写入参数,它会默认给父组件传递 来自this.$emit(“son”, 参数2)中的参数2,如果数据很复杂,可以使用对象或数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值