Vue中利用组件化的思想把页面拆成一个个小的功能块(组件),每个功能块完成自己这部分独立的功能。

一、组件的基本使用

注册组件的基本步骤

  1. 创建组件构造器 – 调用Vue.extend()方法
  2. 注册组件 – 调用Vue.component()方法
  3. 使用组件 – 在Vue实例的作用范围内使用

代码示例:

<div id="app">
    <!-- 3.使用组件 -->
      <mycpn></mycpn>
      <mycpn></mycpn>
      <mycpn></mycpn>
    </div>

    <script src="../js/vue.js"></script>
    <script>     
      // 1.创建组件构造器对象
    const cpnC = Vue.extend({
      template:  `
        <div>
          <h2>我是模板1</h2>
          <p>hello,world!</p>
          <p>1234556</p>
        </div>`
      // ES6语法中使用``,相比引号来说可以实现不用+号换行
    })

      // 2.注册组件,传入组件名与组件对象
    Vue.component('mycpn' , cpnC)

// 注意:不能把创建和注册组件放到new Vue的后面
    const app = new Vue({
      el:'#app',
      data:{
        message:'hello'
      }
    })
    </script>

步骤解析:

  1. Vue.extend():创建一个组件构造器,传入template代表组件模板,比如要显示的html代码。此方法在Vue2.x中直接使用语法糖。
  2. Vue.component():注册组件,并给组件一个标签名,所以传入1.组件标签名,2.组件构造器对象。
  3. 使用组件必须在挂载到Vue实例下。

二、全局组件与局部组件

通过以下代码示例:

<div id="app">
      <cpn></cpn>  
    </div>
    <script>
      const cpn1 = Vue.extend({
        template:`
        <h2>我是模板1</h2>
        `
      })

      // 全局注册(可在多个Vue实例中使用)
      Vue.component('cpn',cpn1)

    const app = new Vue({
      el:'#app',
      data:{
        message:'hello'
      },
      // 局部注册(只能在该实例下使用)
      components:{
        // 组件标签名:组件对象
        cpn:cpn1
      }
    })
    </script>

在实际开发中,一般只创建一个实例,局部组件使用的更多。

三、父组件和子组件

组件与组件之间存在层级关系,在一个组件中可以对另一个组件进行注册,被注册的组件为子组件,注册的组件为父组件。
代码示例:

<div id="app">
      <cpn2></cpn2>
    </div>

    <script>
    // 1.创建组件1-子组件
    const cpn1 = Vue.extend({
      template:`
      <div>
        <h2>模板1</h2>
      </div>
      `
    })
    // 2.创建组件2-父组件
    const cpn2 = Vue.extend({
      template:`
      <div>
        <h2>模板2</h2>
        <cpn1></cpn1>
      </div>
      `,
      // 在父组件中注册子组件
      components:{
        cpn1 : cpn1
      }
    })

    // root组件
    const app = new Vue({
      el:'#app',
      data:{
        message:'hello'
      },
      components:{
        cpn2: cpn2
      }
    })
    </script>

四、注册组件语法糖

在开发中常常省略extend方法,直接使用component方法进行组件的注册。
代码示例:

Vue.component('cpn1',{
      template:`
      <div>
        <h2>模板1</h2>
      </div>
      `
    })

父子组件写法:

Vue.component('cpn1',{
      template:`
      <div>
        <h2>模板1</h2>
        <cpn2></cpn2>
      </div>
      `,
      components:{
        'cpn2':{
          template:`
            <div>
             <h2>模板1</h2>
            </div>
                  `
        }
      }
    })

五、组件模板分离的写法

为了使代码更加简洁美观,在开发中常常将组件模板template的内容分离出去,有以下两种方法:

  1. script标签
<script type="text/x-template" id='cpn'>
    <div>
      <h2>我是模板1</h2>
    </div>
    </script>
  1. template标签
<template id="cpn">
      <div>
        <h2>我是模板2</h2>
      </div>
    </template>

分离出去后,在注册组件时通过id调用即可:

Vue.component('cpn',{
      template:'#cpn'
    })

六、组件中的data属性

组件不能直接调用Vue实例中的data属性,但是在component方法中也含有自己的data属性用于存放自己的属性(也有methods等属性),这个data必须是一个函数,并且返回一个保存着数据的对象。

<div id="app">
      <cpn></cpn>
    </div>
    <template id="cpn">
      <div>
        <h2>{{message}}</h2>
      </div>
    </template>
    <script>


    Vue.component('cpn',{
      template:'#cpn',
      data(){
        return {
          message:'我是组件的data'
        }
      }
    })

组件中的data为函数的原因是能够使用组件时,data函数返回属于实例自己的对象,从而不会相互影响,下面的代码示例了一个用组件封装的计数器:

<div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
    </div>
  <template id="cpn">
    <div>
    <h2>当前计数:{{count}}</h2>
    <button @click='sub' :disabled = 'this.count<=0'>-</button>
    <button @click='add'>+</button>
    </div>
  </template>
    <script>
      // 1.注册组件
    Vue.component('cpn',{
      template:'#cpn',
      data(){
        return {
          count:0
        }
      },
      methods: {
        sub(){
          this.count--
        },
        add(){
          this.count++
        }
      },
    })
    const app = new Vue({
      el:'#app',
      data:{
      }
    })
    </script>

七、组件之间的通信

在开发中,常常会遇到页面大组件从服务器请求了很多数据,然后由下面的子组件进行展示的情况,这个时候就是直接让父组件将数据传递给子组件。
子组件是不能引用父组件或者Vue实例(根组件)的数据的,而是通过以下两种方式:

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

7.1 props基本用法(父传子)

props支持使用数组与对象类型,下面例子演示了数组写法:

<div id="app">
      <!-- 3.将根组件的值动态绑定给子组件的属性 -->
      <cpn :childmsg='message' :childmkt='milktea'></cpn>
    </div>
    <template id="cpn">
      <div>
        <h2>{{childmsg}}</h2>
        <ul>
         <li v-for='itm in childmkt'>{{itm}}</li>
        </ul>
      </div>
    </template>
    <script>
// 2.注册子组件,利用props定义一个数组属性
    const cpn = {
      template:'#cpn',
      data() {
        return {
          
        }
      },
      props:['childmsg','childmkt']
    }
// 1.在根组件中绑定子组件,并传入message和milktea的值
    const app = new Vue({
      el:'#app',
      data:{
        message:'hello',
        milktea:['一点点','烧仙草','茶百道','丸摩堂']
      },
      components:{
        cpn
      }
    })
    </script>

7.2 props数据验证

当需要对props数据进行验证时,需要使用对象写法。
验证支持:String、Number、Boolean、Array、Object、Date、Function、Symbol,也支持自定义构造函数。

props:{
        // 1.类型限制
        childmsg: String,
        // 2.提供一些默认值,以及定义是否必须传值
        childstr:{
          type: String,
          default: '默认值',
          required: true
        },
        // 3.类型是对象或者数组时,默认值必须是一个函数
        childmkt:{
          type: Array,
          default(){
            return []
          }
        }
      }

注:在props中定义属性名时,尽量避免使用驼峰命名法,如果使用了驼峰命名法,在把父组件的值绑定给子组件时,子组件名需要在大写字母前加“-”。
示例,当子组件props中含有一个“cMessage”属性,需要进行如下绑定方法:

<div id="app">
      <cpn :c-message = 'Message'></cpn>
    </div>

7.3 自定义事件(子传父)

子组件传给父组件,常常通过以下步骤:

  1. 在子组件中通过$emit 发送自定义事件;
  2. 在父组件DOM中使用v-on监听子组件的自定义事件;
  3. 在父组件实例中通过methods处理子组件的自定义事件。

代码示例:

<div id="app">
    <!-- 2.在父组件DOM中,通过v-on监听子组件的自定义事件 -->
    <cpn @itmclick='cpnClick'></cpn>
  </div>

  <template id="cpn">
    <div>
    <button v-for = 'itm in categories' @click = 'btnclk(itm)'
    >{{itm.name}}</button>
    </div>
  </template>
  <script>
  
  const cpn = {
    template:'#cpn',
    data() {
      return {
        categories:[
          {id:'a',name:'热门推荐'},
          {id:'b',name:'手机数码'},
          {id:'c',name:'家用家电'},
          {id:'d',name:'电脑办公'}
        ]
      }
    },
    methods: {
      btnclk(itm){
        // 1.子组件发送事件,自定义事件
        this.$emit('itmclick',itm)
      }
    },
  }

  const app = new Vue({
    el:'#app',
    data:{
          },
    components:{
      cpn
    },
    methods:{
      // 3.在父组件中进行处理
      cpnClick(itm){
        console.log(itm);
      }
    }
  })
  </script>

7.4 父子组件通信示例

下面的代码演示了子组件获取父组件data中的参数值,并且通过修改input输入框里的value,使用$emit方法将inpur 中的event事件value传给父组件,达到修改父组件中data的值的目的。

<div id="app">
      <cpn :cnum1='num1' :cnum2='num2'
      @cnum1change='num1change'></cpn>
    </div>
    <template id="cpn">
      <div>
        <h2>data:{{dnum1}}</h2>
        <h2>props:{{cnum1}}</h2>
        <input type="text" :value='dnum1' @input = 'cnum1input'>
      </div>
    </template>
  
    <script>
    const app = new Vue({
      el:'#app',
      data:{
        num1:1,
        num2:0
      },
      methods: {
        num1change(value){
          this.num1 = parseFloat(value)
        }
      },
      components:{
        cpn:{
          template:'#cpn',
          props:{
            cnum1:Number,
            cnum2:Number
          },
          data() {
            return {
              dnum1:this.cnum1,
              dnum2:this.cnum2
            }
          },
          methods: {
            cnum1input(event){
              this.dnum1 = event.target.value;
              this.$emit('cnum1change', this.dnum1)
            }
          },
        }
      }
    })
    </script>

运行效果:

Vue3 typescript extend Vue 组件 vue组件实现_vue

7.5 父子组件的访问方式

父子组件也可以相互之间访问,

  1. 父组件访问子组件:$children$refs
  2. 子组件访问父组件:$parent
7.5.1 父访问子:$children$refs

在根组件中添加了一个子组件cpn后,可以在根组件中通过$children的方法直接获取到子组件,DOM中存在多个子组件时,可加上索引,也可以直接获取到子组件中的属性值。父组件的methods如下:

methods: {
        btnclk(){
          console.log(this.$children[0]);
          console.log(this.$children[0].name);
        }

触发点击函数时,会有以下效果:

Vue3 typescript extend Vue 组件 vue组件实现_自定义事件_02

在开发中,常常使用$refs方法用于父组件访问子组件,只需要在DOM中的组件标签里添加上ref属性:

<cpn ref='cpnref'></cpn>

再在父组件的method中利用以下方法获取ref值,就可以产生同样的效果:

btnclk(){
          console.log(this.$refs.cpnref)
          console.log(this.$refs.cpnref.name)
        }
7.5.2 子访问父:$parent$root

同样的,在子组件的methods中,可通过$parent访问父组件,也可通过$root直接访问根组件。
子组件的methods如下:

methods: {
            cbtnclk(){
              console.log(this.$parent.message)
              console.log(this.$root)
            }
            }

效果如下:

Vue3 typescript extend Vue 组件 vue组件实现_自定义事件_03