在第一篇入门文章中对 Vue.js 有了一个初步的认识,并且能够使用已有的知识完成简单实例的开发尝试——Todo List,如果有兴趣可以到我的Github中查看,感觉小有成就感。
本文则继续入门学习,学习下Vue中重要的内容——组件。
概述
组件是Vue中非常重要的部分,可以用来扩展HTML元素或者用于封装可复用代码,给开发带来更多的灵活性。
注册
组件在使用之前必须要先完成注册,注册有两种方式,全局注册和局部注册,通过全局注册的方式可以使组件在任意Vue实例中使用,而使用局部这次的方式则注册的组件只能用于当前Vue实例中。
全局注册
通过Vue.component(tagName, options)
的方式来注册一个全局的组件。
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
// 使用
<div id="example">
<my-component></my-component>
</div>
局部注册
在Vue实例创建时通过components
属性来注册仅能在当前实例中使用的组件。
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父模板可用
'my-component': Child
}
})
注意
由于HTML的一些限制,一些特殊的HTML标签对包裹的标签进行了限制,这使得使用自定义组件时,若添加在受限的标签内部的话,就会出现不能解析的情况,导致无效,例如<ul>
,<ol>
,<table>
,<select>
。
在这种情况下,可以使用is
属性来解决。
<table>
<tr is="my-row"></tr>
</table>
应当注意,如果您使用来自以下来源之一的字符串模板,这些限制将不适用:
<script type="text/x-template">
- JavaScript内联模版字符串
- .vue 组件
因此,有必要的话请使用字符串模版。
父子组件间通信
在 Vue.js 中,父子组件的关系可以总结为 props down, events up。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。
props
组件实例的作用域是独立的,可以使用props属性来对父组件开放自定义属性。
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 可以用在模板内
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
// 父组件中使用子组件
<child message="hello!"></child>
// 父组件中使用v-bind的方式绑定props
<child :message="hello!"></child>
由于HTML中不区分大小写,若组件中定义含大写字符的属性名称,则在父组件中使用时需要将大写字母转成小写,并用
-
连接单词。
props
是单向绑定的,父组件的数据变更会传递到子组件中,但是子组件的变更并不能传递回父组件。另外,每次父组件的变更都会带来子组件中所有props
的更新。
props
可以设定验证规则,Vue提供了一些默认的规则,当然也支持自定义验证规则。
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
}
}
}
})
events
父组件与子组件约定好事件名称,父组件使用$on(eventName)
在自己的作用域中监听事件是否触发,子组件使用$emit(eventName)
来触发事件,当父组件监听到事件后,就可以对子组件传递来的数据进行处理,更新自己的数据属性了。
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
Slot内容分发
为了解决父组件内容与子组件自己模板混合的问题,Vue引入<slot>
标签,通过该标签,就可以在子组件中定义承载父组件内容的插槽,当父组件对应位置有内容时,渲染父组件内容,没有内容时使用子组件自己定义的内容。
// 子组件 my-component 模板:
<div>
<h2>我是子组件的标题</h2>
<slot>
只有在没有要分发的内容时才会显示。
</slot>
</div>
// 父组件模版:
<div>
<h1>我是父组件的标题</h1>
<my-component>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</my-component>
</div>
// 渲染结果:
<div>
<h1>我是父组件的标题</h1>
<div>
<h2>我是子组件的标题</h2>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</div>
</div>
可以通过对slot
添加name
属性的方式对其命名,这样就可以在一个模板中插入多个slot
。
// 子组件模板
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
// 父组件模版:
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
// 渲染结果为:
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>
特别注意事项
1. 字面量语法和动态语法
<!-- 传递了一个字符串"1" -->
<comp some-prop="1"></comp>
<!-- 传递实际的数字 -->
<comp v-bind:some-prop="1"></comp>
前者直接以字符串的形式进行了数据传递,后者则将值视为表达式计算后传递。
2. 编写可服用组件
Vue 组件的 API 来自三部分 - props, events 和 slots :
- Props 允许外部环境传递数据给组件
- Events 允许组件触发外部环境的副作用
- Slots 允许外部环境将额外的内容组合在组件中。
3.组件命名规则
- 注册组件时可以使用kebab-case,camelCase或TitleCase。
- 在 HTML 模版中,请使用 kebab-case 形式。
- 使用字符串模式时,可以使用 camelCase 、 TitleCase 或者 kebab-case
网友评论