组件是指实现应用中局部功能代码htmlcssjs等)和资源(图片、音频、视频等)的集合

传统的Web应用存在以下缺点:

  • 依赖关系混乱,不易于维护。
  • 代码复用率不高。

CSS文件可以通过外部引入,不存在复用率不高的问题。JS的模块化解决了JS代码复用率不高的问题。而Html想要复用却不是那么容易。

  • 模块化应用:当应用中的JS都以区分模块的形式来编写,那么这个应用就是一个模块化的应用。
  • 组件化应用:当应用中的功能都是以多组件的方式来编写,那么这个应用就是一个组件化的应用。

使用Vue组件可以扩展HTML元素,封装可重用的代码,解决了上述问题。

使用组件系统可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

组件树

在Vue中,组件有两种编写形式:

  • 非单文件组件:一个文件中包含有其它多个组件。
  • 单文件组件:一个文件中只包含有1个组件。

在Vue中使用组件共分为3步:

  1. 定义(创建)组件。
  2. 注册组件:
  • 局部注册。
  • 全局注册。
  1. 使用组件(组件标签)。

组件的注册

组件有两种注册方式:

  • 局部注册:在创建Vue实例时,在其配置对象中的components配置项中定义。

    例如:

    <div id="#app">
      <!-- 3. 使用组件 -->
      <hello></hello>
    </div>
    <script type="text/javascript">
      // 1. 创建组件
      const hello = Vue.extend({
        template: `
          <div>
            <h2>{{msg}}</h2>
          </div>
        `,
        data() {
          return {
            msg: 'Hello Vue.js!',
          }
        },
      });
    
      const vm = new Vue({
        el: "#app",
        // 2. 注册组件(局部注册)
        components: {
          'hello': hello,
          // hello,  // 简写形式
        },
      });
    </script>
    

    components中,组件是以key: value的形式进行注册。key作为组件的名称,而value使用的是组件的实例对象。

    局部注册的组件只能在当前的Vue实例或组件实例中使用。

  • 全局注册:使用Vue原型对象提供的Vue.component()方法。

    Vue.component()接收两个参数:

    1. 参数1:注册组件时使用的key
    2. 参数2:组件的实例对象。

    如上例,将hello组件改为全局注册:

    <div id="#app">
      <!-- 3. 使用组件 -->
      <hello></hello>
    </div>
    <script type="text/javascript">
      // 1. 创建组件
      const hello = Vue.extend({
        template: `
          <div>
            <h2>{{msg}}</h2>
          </div>
        `,
        data() {
          return {
            msg: 'Hello Vue.js!',
          }
        },
      });
    
      // 2. 注册组件(全局注册)
      Vue.component('hello', hello)
    
      const vm = new Vue({
        el: "#app",
      });
    </script>
    

组件的使用与命名

组件在使用时,直接以<component-name>的形式使用。切确地说,component-name是组件在注册时传入的key

如上例中:

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

其实组件标签可以使用闭合标签的形式编写。如<user></user>可以写为<user/>。但是这种用法最好在Vue脚手架中使用。因为不使用脚手架的情况下,闭合标签可能会调职后续组件不能被成功渲染。

关于组件名可以采用以下两种命名方式:

  • kebab-case(烤肉串式):用-连接多个单词。
  • CamelCase(大驼峰命名法,需要Vue脚手架支持):将每个单词的首字母设为大写形式。

在定义组件名时需要注意:

  • 组件名尽可能回避Html中已有的元素名称(Vue会报错)。

  • 可以使用name配置项指定组件在开发者工具中展示的名称。

    如上例:

    const hello = Vue.extend({
      name: 'hello-vue',
      template: `
        <div>
          <h2>{{msg}}</h2>
        </div>
      `,
      data() {
        return {
          msg: 'Hello Vue.js!',
        }
      },
    })
    

    那么无论组件名被定义成什么,hello组件在Vue开发者工具中展示的都是hello-vue这个名称。


非单文件组件

如下是一个组件的示例:

<div id="app">
  <h2>{{msg}}</h2>
  <hr>
  <!-- 3. 编写组件标签 -->
  <site></site>
  <hr>
  <!-- 3. 编写组件标签 -->
  <user></user>
</div>
<script type="text/javascript">
  // 1. 创建site组件
  const site = Vue.extend({  
    template: `
      <div>
        <h2>网站名称:{{name}}</h2>
        <h2>网站地址:{{url}}</h2>
      </div>
    `,
    data() {
      return {
        name: 'Linner\'s Blog',
        url: 'blog.linner.asia',
      }
    },
  });

  // 1. 创建user组件
  const user = Vue.extend({
    template: `
      <div>
        <h2>用户姓名:{{name}}</h2>
        <h2>用户年龄:{{age}}</h2>
      </div>
    `,
    data() {
      return {
        name: '张三',
        age: 18,
      }
    },
  });

  new Vue({
    el: "#app",
    data: {
      msg: 'Hello!'
    },
    // 2. 注册组件(局部注册)
    components: {
      site,
      user,
    },
  });
</script>

创建组件

非单文件组件的创建使用的是Vue.extend()方法。

Vue.extend()的使用与Vue实例的构造函数(new Vue())非常类似,它们都需要传入一个配置对象。并且在Vue构造函数的配置对象中使用的大多配置项,在Vue.extend()的配置对象中同样也能使用(如datamethods等)。

但需要注意的是,Vue.extend()的配置对象中不能使用el配置项;并且它的data配置项需要使用函数式来定义,然后通过在data()return一个对象来作为该组件的data对象。

在定义非单文件组件时,可以将const component = Vue.extend(options)简写为const component = options

如上,将site:使用简写形式定义:

const site = {  
  template: `
    <div>
      <h2>网站名称:{{name}}</h2>
      <h2>网站地址:{{url}}</h2>
    </div>
  `,
  data() {
    return {
      name: 'Linner\'s Blog',
      url: 'blog.linner.asia',
    }
  },
}

使用简写形式时,Vue.extend()vm来调用。

Vue.extend()返回的是一个VueComponent()构造函数。并且每次调用Vue.extend()时,返回的都是一个新的VueComponent()

Vue.extend()的部分源码如下。Vue.extend()在每次被调用时,都会定义一个新的VueComponent()

Vue.extend = function (extendOptions) {
    /* ... */
    
    // VueComponent() 是在 Vue.extend() 中现场定义的
    var Sub = function VueComponent(options) {
        this._init(options);
    };
    
    /* 对Sub进行一系列处理... */
    return Sub;
};

组件配置

使用Vue.extend()创建组件,还需要在配置对象中启用template配置。template的作用是,定义组件的布局结构,也就是组件中可供复用的HTML代码。

在使用template的时候需要注意,template的内容必须仅包含1个根元素,并且不能使用<template>标签作为根元素。

使用Vue.extend()的注意项:

  • 组件配置项与Vue实例配置项基本相同。但是在组件配置中,不能使用el配置。

    组件时作为可以复用的Vue实例,所以它们与根实例(Vue实例)接收的配置项基本相同(包括datacomputedwatchmethodscomponents以及生命周期钩子等)。但是所有的组件最终都要被一个根实例vm所管理,由vm决定它们要服务于哪个容器。并且考虑到组件的复用,所以不使用el,而是使用template来配置可复用的组件模板。

  • 组件中的data要配置成一个函数。

    防止一个组件被多次使用时,存在数据的引用关系。data使用函数来定义,可以确保每个组件实例都可以维护一份由data返回对象的独立拷贝。

组件的嵌套

组件的嵌套使用很简单,直接在组件的配置对象中使用components配置项即可。

例如:

<div id="root"></div>
<script type="text/javascript">
  // 定义user组件
  const user = {
    template: `
      <div>
        <h2>用户姓名:{{name}}</h2>
        <h2>用户年龄:{{age}}</h2>
      </div>
    `,
    data() {
      return {
        name: '张三',
        age: 18,
      }
    },
  }

  // 定义site组件
  const site = {
    template: `
      <div>
        <h2>网站名称:{{name}}</h2>
        <h2>网站地址:{{url}}</h2>
        <user></user>
      </div>
    `,
    data() {
      return {
        name: 'Linner\'s Blog',
        url: 'blog.linner.asia',
      }
    },
    // 注册组件(局部)
    components: {
      user,
    },
  }

  // 定义hello组件
  const hello = {
    template: `
      <h1>{{msg}}</h1>
    `,
    data() {
      return {
        msg: 'Hello Vue.js',
      }
    },
  }

  // 定义app组件
  const app = {
    template: `
      <div>
        <hello></hello>
        <hr>
        <site></site>
      </div>
    `,
    components: {
      hello,
      site,
    },
  }

  const vm = new Vue({
    el: "#root",
    template: `
      <app></app>
    `,
    // 注册组件(局部)
    components: {app},
  });
</script>

组件实例对象

在组件的一些配置项中(如datamethodswatchcomputed等)的函数,this通常指向的是VueComponent()创建出来的实例对象。而使用new Vue()时,如上的一些配置项中的函数,它们的this通常指向的是Vue实例对象(vm)。

VueComponent()创建出来的实例对象即为组件实例对象,又可称为VueComponent对象(在代码中可使用缩写形式vc)。

虽然vc的结构和vm十分相似(vc也使用了数据代理和数据劫持),但是它们是两种不同的对象。

在使用组件标签时,每一个组件标签都对应着一个组件实例每一个组件实例都是由它对应的VueComponent()构造函数所创建

例如:

<div id="app">
  <site></site>
  <hr>
  <user></user>
  <user></user>
</div>

其中所有的<site></site>都对应着一个site组件的VueComponent()构造函数,所有的<user></user>都对应着一个user组件的VueComponent()构造函数。也就是说,每一个组件都对应一个VueComponent()构造函数

而上方的user组件被使用了两次,也就是两个<user></user>。其中每一个<user></user>都对应着一个使用userVueComponent()所创建出来的VueComponent对象。也就是说,每使用一次<user></user>,Vue都会使用userVueComponent()创建出来一个组件实例。即每一个组件标签对应着一个组件实例

当组件被注册时(无论是局部注册还是全局注册),它们都会被vm所管理。并且当组件被使用时,它们的VueComponent对象都会被保存在vm.$children中。

vm.$children是数组类型,保存了实例中使用到的所有VueComponent对象。

而组件在使用子组件时,同vm一样,会将其保存在当前组件实例对象vc$childrenvc.$children)中。


Vue 原型链

Vue 的原型链如下:

Vue.js 原型链

所以在Vue中,Vue.extend(options).prototype.__proto__ === Vue.prototypeVue.extend(options)返回的是VueComponent构造函数)。


单文件组件

单文件组件请查看 Vue 项目开发