第3章 vue组件开发

1. 组件开发

在vue中,组件是最重要的组合部分,官方中定义组件为可复用的vue实例,分为全局组件和局部组件。

1.1 全局组件

使用全局组件的步骤如下:

  • 调用vue.extend()创建一个组件构造器,该构造器中有一个选项对象的template属性可以用来定义组件要渲染的HTML
  • 使用vue.component()注册组件,需要提供2个参数:组件的标签和组件构造器。vue.component()内部会调用组件构造器,创建一个组件实例
  • 将组建挂载到某个vue实例下

因为组件是可复用的vue实例,所以它们也能接收data、computed、watch、methods以及生命周期钩子等选项

<div id="demo">
    <haha></haha>
</div>
<script type="text/javascript">
    var red = Vue.extend({
        template: "<span style='color: red;'>全局组件</span>"
    });
    Vue.component('haha',red);
    var demo = new Vue({
        el: "#demo"
    })
</script>

1.2 局部组件

调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。 如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。

<div id="demo">
    <haha></haha>
</div>
<script type="text/javascript">
    var red = Vue.extend({
        template: "<span style='color: red;'>局部组件</span>"
    });
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:red
        }
    })
</script>

虽然上面的组件是在某个具体的vue实例下注册的,但是组件构造器还是全局的,这个并不是完全意义上的局部组件,下面这种组件才是真正意义上的局部组件。

<div id="demo">
    <haha></haha>
</div>
<script type="text/javascript">
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:{
                template:'<span style="color: red;">局部组件</span>'
            }
        }
    })
</script>

1.3 组件模板

可以通过<template>标记声明组件,再通过全局或局部注册组件来使用。
组件中data不是属性,是方法,需要将数据通过返回值进行返回

<div id="demo">
    <haha></haha>
</div>
<template id="abc">
    <div @click="test1" style="cursor: pointer;">{{message}}</div>
</template>
<script type="text/javascript">
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:{
                data(){
                    return {
                        message: 'init info'
                    }
                },
                methods:{
                    test1(){
                        if(this.message == 'init info'){
                            this.message = 'click info'
                        }else{
                            this.message = 'init info'
                        }   
                    }
                },
                template:"#abc"
            }
        }
    })
</script>

2. 组件通信

2.1 父子组件

当继续在组件中写组件,形成组件嵌套的时候,就是所谓的父子组件。

<div id="demo">
    <haha></haha>
</div>

<template id="haha">
    <div>
        <h2>{{message}}</h2>
        <xixi></xixi>
    </div>
</template>

<template id="xixi">
    <div>
        <h3>{{info}}</h3>
    </div>
</template>

<script type="text/javascript">
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:{
                data(){
                    return {
                        message: '父组件'
                    }
                },
                template:"#haha",
                components:{
                    xixi:{
                        data(){
                            return {
                                info:'子组件'
                            }
                        },
                        template: "#xixi"
                    }
                    
                }
            }
        }
    })
</script>

2.2 子组件获取父组件的数据

在vue中,组件实例的作用域是孤立的,默认情况下,父子组件的数据是不能共享的,也就是说,子组件是不能直接访问父组件的数据的。为此,vue给我们提供了一个数据传递的选项prop,用来将父组件的数据传递给子组件。

  1. 父组件template中,调用子组件位置通过:msg="message"表示将父组件中的data:message传递给子组件,名字为msg
  2. 子组件components中通过props声明['msg']表示接收父组件推送的数据,子组件template直接{{msg}}进行调用
<div id="demo">
    <haha></haha>
</div>

<template id="haha">
    <div>
        <h2>{{message}}</h2>
        <xixi :msg="message"></xixi>
    </div>
</template>

<template id="xixi">
    <div>
        <h3>{{info}} -> {{msg}}</h3>
    </div>
</template>

<script type="text/javascript">
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:{
                data(){
                    return {
                        message: '父组件'
                    }
                },
                template:"#haha",
                components:{
                    xixi:{
                        data(){
                            return {
                                info:'子组件'
                            }
                        },
                        props:['msg'],
                        template: "#xixi"
                    }
                    
                }
            }
        }
    })
</script>

2.3 父组件获取子组件的数据

父组件获取子组件需要子组件事件驱动,通过触发一个事件将自身的数据发送给父组件。
步骤:
1.在子组件的methods中编写send方法,其中通过emit函数将需要传递的数据绑定一个名字“child-msg”
2.在父组件的template中调用子组件的标记处,通过@child-msg指向父组件的绑定函数"getMsg"
3.在父组件的methods中编写getMsg函数,通过方法参数接收传递过来的数据,并将其赋值给某个data(cmsg)
4.通过使用cmsg来使用子组件的数据。

<div id="demo">
    <haha></haha>
</div>

<template id="haha">
    <div>
        <h2>{{message}}</h2>
        <xixi :msg="message" @child-msg="getMsg"></xixi>
        <div>{{cmsg}}</div>
    </div>
</template>

<template id="xixi">
    <div @click="send">
        <h3>{{info}} -> {{msg}}</h3>
    </div>
</template>

<script type="text/javascript">
    var demo = new Vue({
        el: "#demo",
        components:{
            haha:{
                data(){
                    return {
                        message: '父组件',
                        cmsg:''
                    }
                },
                methods:{
                    getMsg(msg){
                        this.cmsg = msg;
                    }
                },
                template:"#haha",
                components:{
                    xixi:{
                        data(){
                            return {
                                info:'子组件数据'
                            }
                        },
                        props:['msg'],
                        template: "#xixi",
                        methods:{
                            send(){
                                this.$emit('child-msg',this.info);
                            }
                        }
                    }
                    
                }
            }
        }
    })
</script>

需要强调的是,父子组件数据时单向更新的

  • 当父组件数据变化时,子组件中的显示会实时更新。
  • 当子组件数据变化时,需要触发事件来驱动父组件数据更新。

2.4 $children和$ref

当一个父组件中存在多个子组件时,可以通过$children来访问其下所有子组件,它会返回一个包含所有子组件的数组

<div id="count">
    <button @click="showmsg">
      显示两个组件的信息
    </button>
    <child1></child1>
    <child2></child2>
    </div>
<template id="child1">
  <div>
    {{ msg }}
  </div>
</template>
<template id="child2">
  <div>
    {{ msg }}
  </div>
</template>
<script>
    Vue.component('child1', {
      template: '#child1',
      data () {
        return {
          msg: '这是子组件1的信息'
        }
      }
    })
    Vue.component('child2', {
      template: '#child2',
      data () {
        return {
          msg: '这是子组件2的信息'
        }
      }
    })
    new Vue({
      el: '#count',
      data: {

      },
      methods: {
        showmsg () {
            for(var i = 0; i < this.$children.length; i++) {
            alert(this.$children[i].msg)
          }
        }
      }
    })
</script>

有时候组件过多的话,就很记清各个组件的顺序与位置,所以通过给子组件一个索引ID来进行快速定位

<div id="count">
    <button @click="showmsg">
      显示两个组件的信息
    </button>
   <child1 ref='c1'></child1>
    <child2 ref='c2'></child2>
    </div>
<template id="child1">
  <div>
    {{ msg }}
  </div>
</template>
<template id="child2">
  <div>
    {{ msg }}
  </div>
</template>
<script>
    Vue.component('child1', {
      template: '#child1',
      data () {
        return {
          msg: '这是子组件1的信息'
        }
      }
    })
    Vue.component('child2', {
      template: '#child2',
      data () {
        return {
          msg: '这是子组件2的信息'
        }
      }
    })
    new Vue({
      el: '#count',
      data: {

      },
      methods: {
        showmsg () {
            alert(this.$refs.c1.msg)
          alert(this.$refs.c2.msg)
        }
      }
    })
</script>

2.5 $parent和$root

子组件通过访问$parent获得其父组件的实例对象

<div id="count">
    父组件中的msg: {{ msg }}
    <child1 ref='c1'></child1>
    <child2 ref='c2'></child2>
</div>
<template id="child1">
  <div>
    {{ msg }}
    <button @click="showpmsg">
      显示父组件msg
    </button>
  </div>
</template>
<template id="child2">
  <div>
    {{ msg }}
  </div>
</template>
<script>
    Vue.component('child1', {
      template: '#child1',
      data () {
        return {
          msg: '这是子组件1的信息'
        }
      },
      methods: {
        showpmsg () {
                alert(this.$parent.msg)
        }
      }
    })
    Vue.component('child2', {
      template: '#child2',
      data () {
        return {
          msg: '这是子组件2的信息'
        }
      }
    })
    new Vue({
      el: '#count',
      data: {
        msg: 'hello parent'
      }
    })
</script>

子组件访问根组件 $root 当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自已。

<div id="count">
    父组件中的msg: {{ msg }}
        <child1 ref='c1'></child1>
    <child2 ref='c2'></child2>
    </div>
<template id="child1">
  <div>
    {{ msg }}
    <cchild></cchild>
  </div>
</template>
<template id="child2">
  <div>
    {{ msg }}
  </div>
</template>
<template id="cchild">
  <div>
    <button @click="showroot">
      showrootmsg
    </button>
  </div>
</template>
<script>
    Vue.component('child1', {
      template: '#child1',
      data () {
        return {
          msg: '这是子组件1的信息'
        }
      },
      methods: {
        showpmsg () {
                alert(this.$parent.msg)
        }
      }
    })
    Vue.component('child2', {
      template: '#child2',
      data () {
        return {
          msg: '这是子组件2的信息'
        }
      }
    })
    Vue.component('cchild', {
      template: '#cchild',
      data () {
        return {
          msg: '这是子组件1的信息'
        }
      },
      methods: {
        showroot () {
                alert(this.$root.msg)
        }
      }
    })
    new Vue({
      el: '#count',
      data: {
        msg: 'hello root'
      }
    })
</script>

推荐阅读更多精彩内容

  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 744评论 0 6
  • 组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应...
    前端一菜鸟阅读 448评论 0 16
  • 第一章 Vue概述 what? Vue是实现UI层的渐进式js框架,核心库关注视图层,简单的ui构建,复杂的路由控...
    fastwe阅读 125评论 0 0
  • 什么是组件? 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装...
    IMUKL阅读 6,860评论 0 13
  • 本文章是我最近在公司的一场内部分享的内容。我有个习惯就是每次分享都会先将要分享的内容写成文章。所以这个文集也是用来...
    Awey阅读 6,627评论 4 66