Vue 组件(上篇)

什么是组件

组件 (Component) 是 Vue.js 最强大的功能之一。组件能够扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些状况下,组件也能够是原生 HTML 元素的形式,以 is 特性扩展。javascript

使用组件

注册

注册一个全局组件,你可使用 Vue.component(tagName, options)html

<html>

<head>
    <meta charset="utf-8" />
    <script type="text/javascript" src='https://unpkg.com/vue'></script>
</head>
<body>
    <div id="example">
        <my-component></my-component>
    </div>
</body>
<script>
    // 注册
    Vue.component('my-component', {
        template: '<div>A custom component!</div>'
    })
    // 建立根实例
    new Vue({
        el: '#example'
    })

</script>

</html>

运行结果:vue

<div id="example">
<div>A custom component!</div>
</div>

局部注册

没必要在全局注册每一个组件。经过使用组件实例选项注册,可使组件仅在另外一个实例/组件的做用域中可用:java

<div id="example-2">
    <child-component></child-component>
</div>

<script>
var Child = {
    template: '<div>A child component!</div>'
}
new Vue({
    el: '#example-2',
    components: {
        'child-component': Child
    }
})
</script>

运行结果:git

<div id="example-2">
<div>A child component!</div>
</div>

若是把 child-component 只能用example-2中,不能放到其余的div中,若是把放到其余的div中就会报错。github

<div id="example">
   <my-component></my-component>
   <child-component></child-component>
</div>

报错提示

DOM 模板解析说明

当使用 DOM 做为模版时 (例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,由于 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。尤为像这些元素<ul>,<ol>,<table>,<select> 限制了能被它包裹的元素,而一些像 <option> 这样的元素只能出如今某些其它元素内部。
在自定义组件中使用这些受限制的元素时会致使一些问题,例如:数组

<table>
  <my-row>...</my-row>
</table>

自定义组件 <my-row> 被认为是无效的内容,所以在渲染的时候会致使错误。变通的方案是使用特殊的 is 属性:浏览器

<table>
  <tr is="my-row"></tr>
</table>

应当注意,若是您使用来自如下来源之一的字符串模板,这些限制将不适用:微信

  • <script type="text/x-template">函数

  • JavaScript 内联模版字符串

  • .vue 组件

所以,有必要的话请使用字符串模版。

data 必须是函数

<div id="example-3">
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
</div>

<script>
var data = { counter: 0 }
Vue.component('simple-counter', {
    template: '<button v-on:click="counter += 1">{{ counter }}</button>',
    // 技术上 data 的确是一个函数了,所以 Vue 不会警告,
    // 可是咱们返回给每一个组件的实例的却引用了同一个data对象
    data: function () {
        return data
    }
})
new Vue({
    el: '#example-3'
})
</script>

因为这三个组件共享了同一个 data,所以增长一个 counter 会影响全部组件!这不对。咱们能够经过为每一个组件返回全新的 data 对象来解决这个问题:

data: function () {
    //return data
    return {
        counter: 0
    }
}

如今每一个 counter 都有它本身内部的状态了。

构成组件

在 Vue 中,父子组件的关系能够总结为 props down, events up。父组件经过 props 向下传递数据给子组件,子组件经过 events 给父组件发送消息。看看它们是怎么工做的。

父子组件-w200

Prop

使用prop传递数据

要让子组件使用父组件的数据,咱们须要经过子组件的 props 选项。

<div id="example-4">
    <child message="hello world!"></child>
</div>

<script>
Vue.component('child', {
    // 声明 props
    props: ['message'],
    // 就像 data 同样,prop 能够用在模板内
    // 一样也能够在 vm 实例中像“this.message”这样使用
    template: '<span>{{ message }}</span>'
})

new Vue({
    el: '#example-4'
})
</script>

运行结果:

<div id="example-4">
<span>hello world!</span>
</div>

camelCase vs. kebab-case

HTML 特性是不区分大小写的。因此,当使用的不是字符串模版,camelCased (驼峰式) 命名的 prop 须要转换为相对应的 kebab-case (短横线隔开式) 命名:

<div id="example-5">
    <child2 my-message="hello world message!"></child2>
</div>

<script>
    Vue.component('child2', {
        // 声明 props
        props: ['myMessage'],
        // 就像 data 同样,prop 能够用在模板内
        // 一样也能够在 vm 实例中像“this.message”这样使用
        template: '<span>{{ myMessage }}</span>'
    })

    new Vue({
        el: '#example-5'
    })

</script>

运行结果:

<div id="example-5">
<span>hello world message!</span>
</div>

若是把child2中的 my-message 改成 myMessage,

<child2 myMessage="hello world message!"></child2>

错误提示:

错误提示

动态 Prop

在模板中,要动态地绑定父组件的数据到子模板的 props,与绑定到任何普通的HTML特性相相似,就是用 v-bind。每当父组件的数据变化时,该变化也会传导给子组件

<div id="example-6">
    <input v-model="parentMsg">
    <br>
    <child2 v-bind:my-message="parentMsg"></child2>
</div>

<script>
Vue.component('child2', {
    // 声明 props
    props: ['myMessage'],
    // 就像 data 同样,prop 能够用在模板内
    // 一样也能够在 vm 实例中像“this.message”这样使用
    template: '<span>{{ myMessage }}</span>'
})


new Vue({
    el: '#example-6',
    data:{
        parentMsg:'Message from parent',
    }
})
</script>

运行结果:

运行结果

字面量语法 vs 动态语法

<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>

由于它是一个字面 prop,它的值是字符串 "1" 而不是 number。若是想传递一个实际的 number,须要使用 v-bind,从而让它的值被看成 JavaScript 表达式计算:

<div id="example-7">
    <comp v-bind:some-prop="1"></comp>
</div>

<script>
Vue.component('comp', {
    // 声明 props
    props: ['someProp'],
    template: "<span>{{ someProp +'----'+ typeof someProp }}</span>"
})
new Vue({
    el: '#example-7'
})
</script>

单向数据流

prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是不会反过来。这是为了防止子组件无心修改了父组件的状态——这会让应用的数据流难以理解。

Prop 验证

咱们能够为组件的 props 指定验证规格。若是传入的数据不符合规格,Vue 会发出警告。当组件给其余人使用时,这颇有用。
要指定验证规格,须要用对象的形式,而不能用字符串数组:

<div id="example-8">
    <example prop-a="11231" prop-c='asda'></example>
</div>

<script>
Vue.component('example', {
    props: {
        // 基础类型检测 (`null` 意思是任何类型均可以)
        propA: Number,
        // 多种类型
        propB: [String, Number],
        // 必传且是字符串
        propC: {
            type: String,
            required: true
        },
        // 数字,有默认值
        propD: {
            type: Number,
            default: 100
        },
        // 数组/对象的默认值应当由一个工厂函数返回
        propE: {
            type: Object,
            default: function () {
                return { message: 'hello' }
            }
        },
        // 自定义验证函数
        propF: {
            validator: function (value) {
                return value > 10
            }
        }
    },
    template: "<span> {{'propA: '+ propA +' propC:'+propC}} </span>"
})

new Vue({
    el: '#example-8'
})
</script>

错误提示:

错误提示

propA 类型是number,而咱们传递给它的是个字符串,须要以下修改

<example v-bind:prop-a="11231" prop-c='asda'></example>

// :prop-a 是 v-bind:prop-a的缩写
<example :prop-a="11231" prop-c='asda'></example>

若是把prop-c去掉,格式以下

<example :prop-a="11231" </example>

错误提示:

错误提示

这是因为propC required: true 是必传的。

type 能够是下面原生构造器:

  • String

  • Number

  • Boolean

  • Function

  • Object

  • Array

  • Symbol

type 也能够是一个自定义构造器函数,使用 instanceof 检测。
当 prop 验证失败,Vue 会在抛出警告 (若是使用的是开发版本)。注意 props 会在组件实例建立以前进行校验,因此在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还没法使用。

非 Prop 属性

所谓非 prop 属性,就是它能够直接传入组件,而不须要定义相应的 prop。
明确给组件定义 prop 是传参的推荐方式,但组件的做者并不总能预见到组件被使用的场景。因此,组件能够接收任意传入的属性,这些属性都会被添加到组件的根元素上。
例如,第三方组件 bs-date-input,当它要和一个 Bootstrap 插件互操做时,须要在这个第三方组件的 input 上添加 data-3d-date-picker 属性,这时能够把属性直接添加到组件上 (不须要事先定义 prop):

<bs-date-input data-3d-date-picker="true"></bs-date-input>

添加属性 data-3d-date-picker="true" 以后,它会被自动添加到 bs-date-input 的根元素上

替换/覆盖现有的特性

<div id="example-9">
    <bs-date-input data-3d-date-picker="true" class="date-picker-theme-dark"></bs-date-input>
</div>

<script>
Vue.component('bs-date-input',{
    template: '<input type="date" class="form-control">'
})

new Vue({
    el: '#example-9'
})
</script>

运行结果:

<div id="example-9">
<input type="date" class="form-control date-picker-theme-dark" data-3d-date-picker="true">
</div>

对于多数特性来讲,传递给组件的值会覆盖组件自己设定的值。即例如传递 type="large" 将会覆盖 type="date" 且有可能破坏该组件!索性咱们对待 class 和 style 特性会更聪明一些,这两个特性的值都会作合并 (merge) 操做,让最终生成的值为:form-control date-picker-theme-dark。

源码: 源码下载

Vue组建(下篇)

社群品牌:从零到壹全栈部落
定位:寻找共好,共同窗习,持续输出全栈技术社群
业界荣誉:IT界的逻辑思惟
文化:输出是最好的学习方式
官方公众号:全栈部落
社群发起人:春哥(从零到壹创始人,交流微信:liyc1215)
技术交流社区:全栈部落BBS
全栈部落完整系列教程:全栈部落完整电子书学习笔记

关注全栈部落官方公众号,每晚十点接收系列原创技术推送
相关文章
相关标签/搜索