一、自定义事件
父组件通过prop给子组件传递数据,子组件则通过自定义事件和父组件通信。
1.使用v-on绑定自定义事件
每个vue实例都实现了事件接口,即
a.使用$on(eventName) 监听事件
b.使用$emit(eventName,optionalPayload)触发事件
父组件可以在使用子组件的地方直接使用v-on来监听子组件出触发的事件,不能使用$on监听
子组件释放的事件,而必须在模板里直接用v-on绑定。
<div id="example1">
<p>{{total}}</p>
<!--子组件触发事件告知父组件,父组件触发自己的处理事件-->
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('button-counter', {
template: '<button v-on:click="incrementCounter">{{counter}}</button>',
data: function () {
return {
counter:0
}
},
methods: {
incrementCounter: function () { //子组件本身的处理事件
this.counter += 1;
this.$emit('increment'); //触发事件告知父组件
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1;
}
}
});
</script>
2.使用载荷数据
<div id="example1">
<p v-for='msg in message'>{{msg}}</p>
<button-msg v-on:message="handleMessage"></button-msg>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('button-msg', {
template: '<div><input type="text" v-model="message" /><button v-on:click="handleSendMessage">send</button></div>',
data: function () {
return {
message: '子组件数据'
}
},
methods: {
handleSendMessage: function () { //子组件本身的处理事件
this.$emit('message',{message: this.message}); //触发事件告知父组件,并传入数据给父组件
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
message: []
},
methods: {
handleMessage: function (payload) {
this.message.push(payload.message);//父组件监听到事件,接受子组件传过来的数据
}
}
});
</script>
3.给组件绑定原生事件
若果要在组件的根元素上监听一个原生事件,可以使用v-on的修饰符 .native 。
如下:只是监听子组件的点击事件。
<my-component v-on:click.native="doTheThing"></my-component>
4.使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件,使用v-model来进行数据双向绑定。
<input v-model="something">
<!--上面的示例只是下面的语法糖-->
<input v-bind:value="something" v-on:input="something=$event.target.value">
<!--在组建建中使用 v-model 相当于下面的简写-->
<custom-input v-bind:value="something" v-on:input="something=arguments[0]"></custom-input>
// 实例
<div id="example1">
<!-- 这里相当于给子组件绑定了v-bind:value=price v-on:input="arguments[0]" -->
<currency-input v-model='price'></currency-input>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('currency-input', {
props: ['pricec'],
template: '<span>price:<input type="text" ref="iptId" v-bind:value="pricec" v-on:input="updateValue($event.target.value)" /></span>',
methods: {
updateValue: function (value) {
var formatVal = value.trim()
.slice(
0,
value.indexOf('.') === -1
? value.length
: value.indexOf('.')+3
);
if(formatVal !== value){
this.$refs.iptId.value = formatVal;
//ref被用来给元素或组件注册引用信息,引用信息在$refs对象上
}
this.$emit('input',Number(formatVal));//这里子组件将自己的input时间告知父组件,此时example1.price也已经改变
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
price:10
}
});
</script>
二、使用插槽分发内容
1.内容分发
为了让组件可以组合,需要一种方式来混合父组件的内容与子组件自己的模板,这个过程为内容发分。
vue使用特殊的slot元素作为原始内容的插槽。
2.编译作用域
父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译 例如:
<!--无效-->
<child-component v-show="someChildProperty"></child-component>
//如果someChildProperty是子组件的属性,上例不会生效。父组件模板不会感知子组件的状态。
//如果要绑定子组件作用域内的指令到一个组件的根节点,应当在子组件自己的模板里做:
Vue.component('child-component', {
// 有效,因为是在正确的作用域内
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
3.单个插槽
除非子组件模板包含至少一个slot口,否则父组件的内容将会被丢弃,当子组件模板只有一个没有属性的插槽时,
父组件传入的整个内容片段将插入到插槽所在的DOM位置,并替换掉插槽便签本身。
最初在slot标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只用在宿主元素为空,
且没有要插入的内容时才显示备用内容。例如:
<div id="example1">
<h4>我是父组件的标题</h4>
<my-component>
<p>我是父组件的内容</p>
</my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('my-component', {
template:'<div><h4>我是子组件的标题</h4><slot>只有在没有要分发的内容时才会显示</slot></div>'
});
var example1 = new Vue({
el: '#example1',
data: {
}
});
</script>
// 以上代码会显示为
<div id="example1">
<h4>我是父组件的标题</h4>
<div>
<h4>我是子组件的标题</h4>
<p>我是父组件的内容</p>
</div>
</div>
// 如果子组件内没有slot插槽,父组件my-component里面的
<p>我是父组件的内容</p>
// 将会被丢弃,不会显示,若果父组件my-component里面没有任何内容,即没有p元素时,slot里面的内容才会显示。
4.具名插槽
slot元素可以用name特殊配置如何分发内容。多个插槽可以用不同的名字,具名插槽将匹配内容片段中有对应slot
特性的元素。仍然可以有一个匿名插槽,它是默认插槽,作为找不到匹配的内容片段的备用插槽。如果没有默认插槽,
这些找不到匹配的内容片段将被抛弃。例如:
<div id="example1">
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落</p>
<p>另一个主要段落</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var html=
'<div class="container">'+
'<header>'+
'<slot name="header"></slot>'+
'</header>'+
'<main>'+
'<slot></slot>'+
'</main>'+
'<footer>'+
'<slot name="footer"></slot>'+
'</footer>'+
'</div>';
Vue.component('app-layout', {
template: html
});
var example1 = new Vue({
el: '#example1',
data: {
}
});
</script>
// 将会渲染为:
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>
// 这就是具名插槽匹配父组件中具有对应slot特性的元素。子组件main中的匿名插槽,作为没有slot特性元素的备用
// 插槽。 若果子组件中没有那个匿名插槽,父组件的
<p>主要内容的一个段落</p>
<p>另一个主要段落</p>
// 会被抛弃不显示。
5.作用域插槽
作用域插槽是一种特殊类型的插槽,用作一个(能被传递数据的)可重用模板,来替代已经渲染好的元素。
在子组件中,只需要将数据传递到插槽,就像prop传递给组件一样。例如:
<div id="example1">
<div class='parent'>
<child>
<div slot-scope="props">
<span>hello from parent</span>
<span>{{props.text}}</span>
</div>
</child>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var html=
'<div class="child">'+
'<slot text="hello from child"></slot>'+
'</div>';
Vue.component('child', {
template: html
});
var example1 = new Vue({
el: '#example1',
data: {
}
});
</script>
// 在父组件中,具有特殊特性slot-scope的元素,表示它是作用域插槽的模板,slot-scope的值将被用作一个临时
// 变量名,此变量接受从子组件传递过来的prop对象,如子组件的slot的text的值。 最终渲染为:
<div class="parent">
<div class="child">
<div>
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>
</div>
四、动态组件
通过使用保留的component元素,并对其is特性进行动态绑定,可以在同一个挂载点动态切换多个组件。例如:
<div id="example1">
<div class='parent'>
<span>选择组件:
<label for="">home <input type="radio" value="component1" v-model="currentView" /></label>
<label for="">footer <input type="radio" value="component2" v-model="currentView" /></label>
<label for="">main <input type="radio" value="component3" v-model="currentView" /></label>
</span>
<component v-bind:is='currentView'>
</component>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var html=
'<div class="child">'+
'<slot text="hello from child"></slot>'+
'</div>';
Vue.component('child', {
template: html
});
var example1 = new Vue({
el: '#example1',
data: {
currentView: 'component1'
},
components: {
'component1':{
template: '<div>动态组件1</div>'
},
'component2': {
template: '<div>动态组件2</div>'
},
'component3': {
template: '<div>动态组件3</div>'
}
}
});
</script>
// 在这里动态改变currentView就可以切换component里面的组件。
1.keep-alive
kepp-alive包囊动态组件时,会缓存不活动的组件实例 ,而不是销毁它们,避免重新渲染。
keep-alive是一个抽象组件,他自身不会渲染一个DOM元素,也不会出现在父组件链中。
当组件在keep-alive内切换时,它的activated和deactivated这两个生命周期函数将会被对应执行。
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
// 注:keep-alive要求被切换到的组件都有自己的名字,不论是通过组件的name选项还是局部或全局注册。