vue.js之组件篇

Vue.js 组件

  • 模块化:是从代码逻辑的角度进行划分的;
  • 组件化:是从UI界面的角度进行划分的。

组件(Component)是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码。

组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树,如下图所示:

图片 1

组件简介

使用Vue.extend来创建全局vue组件:

var com1 = Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>' // 通过template属性,指定了组件要展示的HTML结构
})

组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树:

此时只是声明了一个叫com1的组件,如果要在vue实例中使用组件,还需要通过以下方式来注册组件:

   Vue.component('myCom1', com1)

图片 2

组件的调用: 

  <div id="app">
    <!-- 如果要使用组件,直接把组件的名称以HTML标签的形式,引入到页面中即可 -->
    <my-com1></my-com1>
  </div>

那么什么是组件呢?

 注意事项:

  1. 如果使用 Vue.component 注册组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母,同时,两个单词之间使用 “-” 连接
  2. 如果不使用驼峰,则直接拿名称来使用即可;

组件可以扩展HTML元素,封装可重用的HTML代码,我们可以将组件看作自定义的HTML元素。

其它两种创建组件的方式:

    Vue.component('myCom1', Vue.extend({
      template: '<h3>这是使用Vue.extend创建的组件</h3>'
    }))

    Vue.component('mycom2', {
      template: '<div><h3>这是直接使用Vue.component创建出来的组件</h3>123</div>'
    })

由于组件的篇幅较大,我将会把组件的入门知识分为两篇来讲解,这样也便于各位看官们快速消化。

注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素

组件的创建和注册

在被控制的#app外面,使用template元素,定义组件的HTML模板结构:

  <template id="tmpl">
    <div>
      <h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的智能提示和语法高亮</h1>
    </div>
  </template>

   Vue.component('mycom3', {
      template: '#tmpl' //引用template的id属性
    })

基本步骤

 私有组件:

  <template id="tmpl2">
    <h1>这是私有的 login 组件</h1>
  </template>

    var vm2 = new Vue({
      el: '#app2',
      components: { // 定义实例内部私有组件的
        login: {
          template: '#tmpl2'
        }
      },
    })

Vue.js的组件的使用有3个步骤:创建组件构造器、注册组件和使用组件。

组件中的data和methods:

  1. 组件可以有自己的data数据;
  2. 组件的data和实例的data有点不一样,实例中的data可以为一个对象,但是组件中的data必须是一个方法;
  3. 组件中的data除了必须为一个方法之外,这个方法内部还必须返回一个对象;
  4. 组件中的data数据,使用方式和实例中的data使用方式完全一样

    Vue.component('mycom1', {
      template: '<h1>这是全局组件 --- {{msg}}</h1>',
      data: function () {
        return {
          msg: '这是组件的中data定义的数据'
        }
      }
    })
    

图片 3

 为什么组件中的data必须要是一个方法,演示如下:

Html代码:

  <div id="app">
    <counter></counter>
    <hr>
    <counter></counter>
    <hr>
    <counter></counter>
  </div>
  <template id="tmpl">
    <div>
      <input type="button" value="+1" @click="increment">
      <h3>{{count}}</h3>
    </div>
  </template>

javascript代码:

    var dataObj = { count: 0 }
    //这是一个计数器的组件, 身上有个按钮,每当点击按钮,让data中的count值+1
    Vue.component('counter', {
      template: '#tmpl',
      data: function () {
        // return dataObj
        return { count: 0 }
      },
      methods: {
        increment() {
          this.count++
        }
      }
    })
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });

调用测试结果如下 :

图片 4

每个计数器的count是独立的,如将将返回的值改为:

        return dataObj
        //return { count: 0 }

结果为:

图片 5

每个组件的count是共享的,组件的data必须是一个方法,且必须返回一个对象。要解决的问题就是提高组件的复用性,防止组件与组件之间成员干扰。

下面的代码演示了这3个步骤:

组件的切换:

<body>
  <div id="app">
    <a href="" @click.prevent="flag=true">登录</a>
    <a href="" @click.prevent="flag=false">注册</a>
    <login v-if="flag"></login>
    <register v-else="flag"></register>
  </div>
  <script>
    Vue.component('login', {
      template: '<h3>登录组件</h3>'
    })
    Vue.component('register', {
      template: '<h3>注册组件</h3>'
    })
    var vm = new Vue({
      el: '#app',
      data: {
        flag: true
      },
      methods: {}
    });
  </script>
</body>

演示结果如下,默认显示登录组件,点击注册切换到注册组件,点击登录切换的登录组件。

图片 6

 

 

 

缺点分析:如果在一个需求中需要切换更多的组件,这种方式不利于扩展。

    <!-- Vue提供了component ,来展示对应名称的组件 -->

    <!-- component 是一个占位符, “:is”是属性,可以用来指定要展示的组件的名称 -->

  <div id="app">
    <a href="" @click.prevent="comName='login'">登录</a>
    <a href="" @click.prevent="comName='register'">注册</a>
    <component :is="comName"></component>
  </div>
  <script>
    // 组件名称是字符串
    Vue.component('login', {
      template: '<h3>登录组件</h3>'
    })
    Vue.component('register', {
      template: '<h3>注册组件</h3>'
    })
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        comName: 'login' //当前component中的 :is 绑定的组件的名称
      },
    });
  </script>
<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <!-- 3. #app是Vue实例挂载的元素,应该在挂载元素范围内使用组件-->
      <my-component></my-component>
    </div>
  </body>
  <script src="js/vue.js"></script>
  <script>

    // 1.创建一个组件构造器
    var myComponent = Vue.extend({
      template: '<div>This is my first component!</div>'
    })

    // 2.注册组件,并指定组件的标签,组件的HTML标签为<my-component>
    Vue.component('my-component', myComponent)

    new Vue({
      el: '#app'
    });

  </script>
</html>

组件切换动画:

html代码:

  <div id="app">
    <a href="" @click.prevent="comName='login'">登录</a>
    <a href="" @click.prevent="comName='register'">注册</a>

    <!-- 通过 mode 属性,设置组件切换时候的模式为先出后进-->
    <transition mode="out-in">
      <component :is="comName"></component>
    </transition>

  </div>

  <script>
    // 组件名称是 字符串
    Vue.component('login', {
      template: '<h3>登录组件</h3>'
    })

    Vue.component('register', {
      template: '<h3>注册组件</h3>'
    })
    var vm = new Vue({
      el: '#app',
      data: {
        comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
      },
    });
  </script>

css代码:

  <style>
    .v-enter,
    .v-leave-to {
      opacity: 0;
      transform: translateX(150px);
    }

    .v-enter-active,
    .v-leave-active {
      transition: all 0.5s ease;
    }
  </style>

运行结果如下:

子组件引用父组件的值:

 1     <div id="app">
 2         <com1></com1>
 3     </div>
 4     <script>
 5 
 6             Vue.component
 7             var vm = new Vue({
 8                 el:'#app',
 9                 data:{
10                     msg:"heelo !"
11                 },
12                 methods:{
13                 },
14                 components:{
15                     com1:{
16                         template:'<h1>这是子组件---{{msg}}</h1>'
17                     }
18                 }
19             })
20         </script>

执行结果:

图片 7

默认情况下子组件是不能引用父组件的属性和对象的,我们可以在引用子组件的时候,通过属性绑定的形式(v-bind)的形式,把需要传递给子组件的数据,以属性绑定的形式传递到子组件内部,供子组件使用,并且在props中声明该属性(props中的数据都是通过父组传递过来的,这里的数据是只读的),代码如下:

 1     <div id="app">
 2         <com1 v-bind:parentmsg="msg"></com1>
 3     </div>
 4     <script>
 5         Vue.component
 6         var vm = new Vue({
 7             el: '#app',
 8             data: {
 9                 msg: "heelo !"
10             },
11             methods: {
12             },
13             components: {
14                 data() {
15                     return {
16                         title: '123',
17                         content: 'pppp'
18                     }
19                 },
20                 com1: {
21                     props: ['parentmsg'],
22                     template: '<h1>这是子组件---{{parentmsg}}</h1>'
23                 }
24             }
25         })
26     </script>

图片 8

子组件调用父组件的方法:

请详细看下面代码中的注释

 1  <div id="app">
 2      
 3     <com2 @func="show"></com2>
 4     </div>
 5     <template id="tmpl">
 6         <div>
 7             <h1>这是子组件</h1>
 8             <input type="button" value="这是子组件中的点击按钮" @click="myclick">
 9         </div>
10     </template>
11     <script>
12         //定义了一个字面量类型的组件模板对象
13         var com2 = {
14             template: '#tmpl',
15             data() {
16                 return {
17                     sonmsg: { name: 'wjl', age: 16 }
18                 }
19             },
20             methods: {
21                 myclick() {
22                     //当点击子组件的按钮时,通过$emit拿到父组件传递过来的func
23                     //emit的英文愿意为触发、调用、发射的意思
24                     this.$emit('func', this.sonmsg, 123, 456)
25                 }
26             }
27         }
28         var vm = new Vue({
29             el: '#app',
30             data: {
31                 datamsg: null
32             },
33             methods: {
34                 show(data, data1, data2) {
35                     console.log('调用了父组件的show方法:' + data.name + data1 + data2);
36                 }
37             },
38             components: {
39                 com2
40             }
41         })
42     </script>

可以看到,使用组件和使用普通的HTML元素没什么区别。

执行结果:

图片 9

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2lv2gsiubr6sw

理解组件的创建和注册

我们用以下几个步骤来理解组件的创建和注册:

1. Vue.extend()是Vue构造器的扩展,调用Vue.extend()创建的是一个组件构造器,而不是一个具体的组件实例。

2. Vue.extend()构造器有一个选项对象,选项对象的template属性用于定义组件要渲染的HTML。

3. 使用Vue.component()注册组件时,需要提供2个参数,第1个参数时组件的标签,第2个参数是组件构造器。

  1. Vue.component()方法内部会调用组件构造器,创建一个组件实例。

  2. 组件应该挂载到某个Vue实例下,否则它不会生效。

请注意第5点,以下代码在3个地方使用了<my-component>标签,但只有#app1和#app2下的<my-component>标签才起到作用。

<!DOCTYPE html>
<html>
  <body>
    <div id="app1">
      <my-component></my-component>
    </div>

    <div id="app2">
      <my-component></my-component>
    </div>

    <!--该组件不会被渲染-->
    <my-component></my-component>
  </body>
  <script src="js/vue.js"></script>
  <script>
    var myComponent = Vue.extend({
      template: '<div>This is a component!</div>'
    })

    Vue.component('my-component', myComponent)

    var app1 = new Vue({
      el: '#app1'
    });

    var app2 = new Vue({
      el: '#app2'
    })
  </script>
</html>

图片 10

全局注册和局部注册

调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。

如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。

上面的示例可以改为局部注册的方式:

<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <!-- 3. my-component只能在#app下使用-->
      <my-component></my-component>
    </div>
  </body>
  <script src="js/vue.js"></script>
  <script>
    // 1.创建一个组件构造器
    var myComponent = Vue.extend({
      template: '<div>This is my first component!</div>'
    })

    new Vue({
      el: '#app',
      components: {
        // 2. 将myComponent组件注册到Vue实例下
        'my-component' : myComponent
      }
    });
  </script>
</html>

由于my-component组件是注册在#app元素对应的Vue实例下的,所以它不能在其它Vue实例下使用。

<div id="app2">
  <!-- 不能使用my-component组件,因为my-component是一个局部组件,它属于#app-->
  <my-component></my-component>
</div>

<script>
  new Vue({
    el: '#app2'
  });
</script>

如果你这样做了,浏览器会提示一个错误:

图片 11

父组件和子组件

我们可以在组件中定义并使用其他组件,这就构成了父子组件的关系。

<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <parent-component>
      </parent-component>
    </div>
  </body>
  <script src="js/vue.js"></script>
  <script>

    var Child = Vue.extend({
      template: '<p>This is a child component!</p>'
    })

    var Parent = Vue.extend({
      // 在Parent组件内使用<child-component>标签
      template :'<p>This is a Parent component</p><child-component></child-component>',
      components: {
        // 局部注册Child组件,该组件只能在Parent组件内使用
        'child-component': Child
      }
    })

    // 全局注册Parent组件
    Vue.component('parent-component', Parent)

    new Vue({
      el: '#app'
    })

  </script>
</html>

这段代码的运行结果如下:

图片 12

我们分几个步骤来理解这段代码:

1.var Child = Vue.extend(...)定义一了个Child组件构造器

2.var Parent = Vue.extend(...)定义一个Parent组件构造器

3.components: { 'child-component': Child },将Child组件注册到Parent组件,并将Child组件的标签设置为child-component。

4.template :'<p>This is a Parent component</p><child-component></child-component>',在Parent组件内以标签的形式使用Child组件。

5.Vue.component('parent-component', Parent) 全局注册Parent组件

6.在页面中使用<parent-component>标签渲染Parent组件的内容,同时Child组件的内容也被渲染出来

图片 13

Child组件是在Parent组件中注册的,它只能在Parent组件中使用,确切地说:子组件只能在父组件的template中使用。

请注意下面两种子组件的使用方式是错误的:

1. 以子标签的形式在父组件中使用

<div id="app">
  <parent-component>
    <child-component></child-component>
  </parent-component>
</div>

为什么这种方式无效呢?因为当子组件注册到父组件时,Vue.js会编译好父组件的模板,模板的内容已经决定了父组件将要渲染的HTML。

<parent-component>…</parent-component>相当于运行时,它的一些子标签只会被当作普通的HTML来执行,<child-component></child-component>不是标准的HTML标签,会被浏览器直接忽视掉。

2. 在父组件标签外使用子组件

<div id="app">
  <parent-component>
  </parent-component>
  <child-component>
  </child-component>
</div>

运行这段代码,浏览器会提示以下错误

图片 14

组件注册语法糖

以上组件注册的方式有些繁琐,Vue.js为了简化这个过程,提供了注册语法糖。

使用Vue.component()直接创建和注册组件:

// 全局注册,my-component1是标签名称
Vue.component('my-component1',{
  template: '<div>This is the first component!</div>'
})

var vm1 = new Vue({
  el: '#app1'
})

Vue.component()的第1个参数是标签名称,第2个参数是一个选项对象,使用选项对象的template属性定义组件模板。
使用这种方式,Vue在背后会自动地调用Vue.extend()。

在选项对象的components属性中实现局部注册:

var vm2 = new Vue({
  el: '#app2',
  components: {
    // 局部注册,my-component2是标签名称
    'my-component2': {
      template: '<div>This is the second component!</div>'
    },
    // 局部注册,my-component3是标签名称
    'my-component3': {
      template: '<div>This is the third component!</div>'
    }
  }
})

使用script或template标签

尽管语法糖简化了组件注册,但在template选项中拼接HTML元素比较麻烦,这也导致了HTML和JavaScript的高耦合性。
庆幸的是,Vue.js提供了两种方式将定义在JavaScript中的HTML模板分离出来。

使用<script>标签

<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <my-component></my-component>
    </div>

    <script type="text/x-template" id="myComponent">
      <div>This is a component!</div>
    </script>
  </body>
  <script src="js/vue.js"></script>
  <script>

    Vue.component('my-component',{
      template: '#myComponent'
    })

    new Vue({
      el: '#app'
    })

  </script>
</html>

template选项现在不再是HTML元素,而是一个id,Vue.js根据这个id查找对应的元素,然后将这个元素内的HTML作为模板进行编译。

图片 15

本文由金沙官网线上发布于Web前端,转载请注明出处:vue.js之组件篇

您可能还会对下面的文章感兴趣: