VueJs学习随笔--组件化

组件基础

组件是可复用的 Vue 实例, 每个组件都有一个名字,我们可以直接使用名字来引入一个组件。

定义一个名为 button-counter 的新组件:

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

怎么使用呢?我们添加到前面的案例当中:

<body>
    <div id="example">
        <button v-on:click="count(3)">Add 3</button>
        <p>The button above has been clicked {{ counter }} times.</p>
        <button-counter/>
    </div>
    <script>
        Vue.component('button-counter', {
            data: function () {
                return {
                count: 0
                }
            },
            template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
        })
        var example1 = new Vue({
            el: '#example',
            data: {
                counter: 0
            },
            methods: {
                count: function(add){
                    this.counter = this.counter + add
                }
            }
        })
    </script>
</body>

修改上面的 div 布局文件,多添加几个 button-conter :

<div id="example">
    <button v-on:click="count(3)">Add 3</button>
    <p>The button above has been clicked {{ counter }} times.</p>
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

你会发现每个组件都会独立的维护他的 counter, 因为每一次使用组件,就会有一个新的实例产生。

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
    count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

组件结构

通常一个应用会以一棵嵌套的组件树的形式来组织:

组件结构树

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。上面的示例中我们的组件都只是通过 Vue.component 全局注册的。

Vue.component('my-component-name', {
  // ... options ...
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例中。

组件传递数据

当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。

<body>
    <div id="example">
        <button-counter starttime=1></button-counter>
        <button-counter starttime=2></button-counter>
        <button-counter starttime=3></button-counter>
    </div>
    <script>
        Vue.component('button-counter', {
            props: ['starttime'],
            data: function () {
                return {
                count: 0
                }
            },
            computed: {
                nowtime: function(){
                    return this.starttime + this.count;
                }
            },
            template: '<button v-on:click="count++">You clicked me {{ nowtime }} times.</button>'
        })
        var example1 = new Vue({
            el: '#example'
        })
    </script>
</body>

注意上面我们添加了一个 props 属性,名字叫 starttime, 但是想要的结果却不是我们预期的,这里的 starttime 被作为 string 类型,做了字符串拼接。

修改代码如下:

<body>
    <div id="example">
-       <button-counter starttime=1></button-counter>
-       <button-counter starttime=2></button-counter>
+       <button-counter v-bind:starttime=1></button-counter>
+       <button-counter v-bind:starttime=2></button-counter>
        <button-counter starttime=3></button-counter>
    </div>
    <script>
        Vue.component('button-counter', {
-           props: [starttime],
+           props: {starttime: Number},
            data: function () {
                return {
                count: 0
                }
            },
            computed: {
                nowtime: function(){
+                   console.log(typeof(this.starttime));
+                   console.log(typeof(this.count));
                    return this.starttime + this.count;
                }
            },
            template: '<button v-on:click="count++">You clicked me {{ nowtime }} times.</button>'
        })
        var example1 = new Vue({
            el: '#example'
        })
    </script>
</body>

当然我们也可以给设置一个默认值:

props: {
     starttime: {
         type: Number,
         default: 0
     }
}

但是我们发现了一个问题,需要添加 v-bind 才能转换成 number 值,否则还是 string 类型。

实际上这个 v-bind 是用来动态传递 prop 的,比如一开始我不确定要传什么值给 starttime ,我可以定义一个变量, 比如叫 ithink:

<button-counter v-bind:starttime="ithink"></button-counter>
 var example1 = new Vue({
    el: '#example',
    data: {
        ithink: 88
    }
})

组件根元素原始事件

我们来看一个例子先:

<div id="example">
    <div :style="{ fontSize: postFontSize + 'em' }">测试文字大小</div>
    <button-large v-on:click="largeFont"></button-large>
    <span v-on:click="largeFont">点击放大</span>
</div>
Vue.component('button-large', {
    data: function(){
        return {

        }
    },
    methods: {
        changeFontSize: function(){
            this.postFontSize++
        }
    },
    template: '<button>Enlarge text</button>'
})

var example1 = new Vue({
    el: '#example',
    data: {
        postFontSize: 1
    },
    methods: {
        largeFont: function(){
            console.log(this.postFontSize)
            this.postFontSize++
        }
    }
})

但是你会发现点击 Enlarge text 按钮不起作用,而点击 点击放大 按钮是有效的,如果我们修改如下就会生效:

<button-large v-on:click.native="largeFont"></button-large>

也可以写成这样,因为 @ 是 v-on 的缩写:

<button-large @click.native="largeFont"></button-large>

如果不加 native 修饰符 Vue 会把 onclick 当成组件的自定义事件,而我们需要原生的 onclick 事件就需要添加 native 修饰符。

监听子组件事件

我们再来试想一种场景,假设我们还需要一个按钮用来变小字号:

template: '<div><button>Enlarge text</button><button>Small Text</button></div>'

这样我们点击 Enlarge textSmall Text 都是变大,因为我的 onclick 事件是绑定到 button-large 组件的。如果我们现在只希望点击 Enlarge text 才变大怎么办?

文件 button-large.vue: 给 Enlarge text 按钮绑定 $emit() 事件向上传递。

<template>
    <div>
        <button v-on:click="$emit('largeFont')">Enlarge text</button>
        <button>Small Text</button>
    </div>
</template>
<script>
export default {
    data(){
        return { }
    }
}
</script>

文件 test-large.vue:ButtonLarge 进行 largeFont 自定义事件监听。

<template>
    <div id="example">
        <div :style="{ fontSize: postFontSize + 'em' }">测试文字大小</div>
        <ButtonLarge v-on:largeFont="postFontSize++"></ButtonLarge>
    </div>
</template>
<script>

import ButtonLarge from '@/button-large'

export default {
    components: {
        ButtonLarge
    },
    data(){
        return {
            postFontSize: 1
        } 
    }
}
</script>