前言:前端框架千千万,独有vue占一半
我是风尚,让我们一起坐火箭去学习Vue
第十三章:Vue中的组件(难点)
上章回顾: 今天的Vue中的动画transition就讲到这里啊。
“好家伙,牛,这动画可以,不就是记几个类名?继续”风尚嘚瑟的说。
“可以啊小伙子,来吧,整个难点的,Vue中的组件!!!”老头大声的说。
“来吧来吧”风尚说
Vue.js 组件
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
全局组件
|
|
|
这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
局部注册
局部注册
var Child = {
template: '<div>一个自定义组件</div>'
}
new Vue({
// ...
components: {
// <my-child></my-child> 将只在父组件模板中可用
'my-child': Child
}
})
data 必须是一个函数 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
var counter = {
template:`<button @click="num++">{{num}}</button>`,
data:function(){return {num:1}}
}
new Vue({
el:"#app",
components:{
counter,
}
})
组件调用
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
组件传参
当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。
<child message="hello!"></child>
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 也可以在模板中使用
// 同样也可以在 vm 实例中通过 this.message 来使用
template: '<span>{{ message }}</span>'
})
动态 Prop
你也知道 prop 可以通过 v-bind 动态赋值
<div>
<input v-model="parentMsg">
<br>
<child :my-message="parentMsg"></child>
</div>
如果你想把一个对象的所有属性作为 prop 进行传递,可以使用不带任何参数的 v-bind
todo: {
text: '学习Vue',
isComplete: false
}
<todo-item v-bind="todo"></todo-item>
=
<todo-item v-bind:text="todo.text" v-bind:is-complete="todo.isComplete"></todo-item>
单向绑定
Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会 另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop
在两种情况下,我们很容易忍不住想去修改 prop 中数据:
- Prop 作为初始值传入后,子组件想把它当作局部数据来用;
- Prop 作为原始数据传入,由子组件处理成其它数据输出。
1. 定义一个局部变量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
2. 定义一个计算属性,处理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
验证
们可以为组件的 prop 指定验证规则。如果传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件非常有用。
Vue.component('example', {
props: {
// 基础类型检测 (`null` 指允许任何类型)
propA: Number,
// 可能是多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数值且有默认值
propD: {
type: Number,
default: 100
},
type 可以是下列原生构造函数中的一个:
String
Number
Boolean
Array
Object
Date
Function
Symbol
子组件怎么跟父组件通信
我们知道,父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。
每个 Vue 实例都实现了事件接口,即:
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
<div id="app">
<p>{{ total }}</p>
<counter v-on:add="addTotal"></counter>
<counter v-on:add="iaddTotal"></counter>
</div>
Vue.component('counter', {
template: '<button v-on:click="addCounter">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
addCounter: function () {
this.counter += 1
this.$emit('add')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
addTotal: function () {
this.total += 1
}
}
})
slot 插槽
在使用组件时,我们常常要像这样组合它们:
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
单个插槽
假定 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>
具名插槽
有时我们需要多个插槽。例如,假定我们有一个 app-layout 组件:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
slot 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:
父组件模板
<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>
一个不带 name 的 出口会带有隐含的名字“default”。
“组件讲完了,风尚感觉如何?”老头说。
“啊?发生了啥?”风尚懵了.....