持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
Props
在上一教程中,我们创建了一个Modal.vue组件,组件里面通过硬编码的方式写死了内容,那么我们如何在外部给组件传递参数呢?
<template>
<div class="backdrop">
<div class="modal">
<h1>modal title</h1>
<p>modal content</p>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
}
</script>
props,定义从父组件到自组件的数据流。
<template>
<div class="backdrop">
<div class="modal">
<h1>{{ modalTitle }}</h1>
<p>{{ content }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
props: ['modalTitle', 'content'],
}
</script>
在Modal组件中我们定义了modalTitle和content两个参数,父组件中引用该组件时,传递入参:
<template>
<h1>{{ title }}</h1>
<Modal :modal-title="modalTitle" :content="content" />
</template>
<script>
import Modal from './components/Modal.vue'
export default {
name: 'App',
components: { Modal },
data() {
return {
title: 'Hello, Vue App :)',
modalTitle: '模态弹窗标题',
content: '模态弹窗内容,writed by lostvitta.',
}
},
}
</script>
我们注意到,Modal组件定义的
modalTitle是驼峰的书写格式,而我们在传递参数时用的是kebab-case格式,如果我们想要继续使用驼峰格式,也是可以的。
自定义事件
前面章节我们实现了一个模态弹窗组件,本章节我们通过vue的自定义事件机制实现模态窗的打开与关闭功能。
<template>
<div class="backdrop" @click="closeModal">
<div class="modal">
<h1>{{ modalTitle }}</h1>
<p>{{ content }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
props: ['modalTitle', 'content'],
emits: ['close'],
methods: {
closeModal() {
this.$emit('close')
},
},
}
</script>
我们通过emits定义了组件支持的自定义事件,这里定义了一个close事件。
<template>
<h1>{{ title }}</h1>
<div>
<button @click="toggleModal">打开</button>
</div>
<Modal
v-if="showModal"
:modalTitle="modalTitle"
:content="content"
@close="toggleModal"
/>
</template>
<script>
import Modal from './components/Modal.vue'
export default {
name: 'App',
components: { Modal },
data() {
return {
title: 'Hello, Vue App :)',
modalTitle: '模态弹窗标题',
content: '模态弹窗内容,writed by lostvitta.',
showModal: false,
}
},
methods: {
toggleModal() {
this.showModal = !this.showModal
},
},
}
</script>
我们在父组件中实现了toggleModal方法用来打开会关闭模态窗,并作为@close事件的值传递给了子组件。如此我们可以看到,自定义事件的机制可以使子组件调用父组件中的方法,从而让子组件有了修改父组件数据的能力。
事件修饰符
在前文我们实现的关闭模态窗功能中,我们点击遮罩部分正常关闭了模态窗,但我们点击模态窗内容的时候也关闭了,这显然不是我们想要的功能。在我们实现的dom结构中,遮罩所在的div是作为根节点存在的,其子节点的点击事件都会冒泡在这个div节点上,我们要如何来阻止呢?
<template>
<div class="backdrop" @click.self="closeModal">
<div class="modal">
<h1>{{ modalTitle }}</h1>
<p>{{ content }}</p>
</div>
</div>
</template>
在组件的@click事件中使用self事件修饰符,表示仅党事件发生在当前节点时才触发绑定事件。
插槽Slots
我们实现的Modal组件,组件内容是通过传递字符串参数实现的自定义,如果我们想传递更复杂的内容如html,甚至是一个自定义的vue组件,是无法做到的。而这,Slots可以帮助我们:
<template>
<div class="backdrop" @click.self="closeModal">
<div class="modal">
<slot>我是默认内容</slot>
<div class="actions">
<slot name="links"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
emits: ['close'],
methods: {
closeModal() {
this.$emit('close')
},
},
}
</script>
我们删除了Modal组件中的html内容,换为slot标签(slot是vue的自定义组件),其中一个不具名,另一个具名为links。
<template>
<h1>{{ title }}</h1>
<div>
<button @click="toggleModal">打开</button>
</div>
<Modal v-if="showModal" @close="toggleModal">
<h1>模态弹窗标题</h1>
<p>模态弹窗内容,writed by lostvitta.</p>
<template v-slot:links>
<a href="">登陆</a>
<a href="">注册</a>
</template>
</Modal>
</template>
父组件中引用Modal组件时,采用了双标签闭合的方式,里面自定义一些内容。其中一部分没有用template标签包裹,会被填充到Modal组件的不具名slot内部,另一部分使用template包裹并定义了v-slot:links属性,这与组件内部的具名links对应。
而当我们什么都不传时,则会渲染默认内容:
<Modal v-if="showModal" @close="toggleModal"> </Modal>
传送门Teleport
我们的Modal是挂载在div#app上的,如果我们希望挂载在#app之外的div上呢?比如,在public/index.html文件中:
<div id="app"></div>
<div id="modal"></div>
在我们的App.vue组件中使用teleport,定义to属性关联div#modal:
<template>
<h1>{{ title }}</h1>
<div>
<button @click="toggleModal">打开</button>
</div>
<teleport to="#modal">
<Modal v-if="showModal" @close="toggleModal">
<h1>模态弹窗标题</h1>
<p>模态弹窗内容,writed by lostvitta.</p>
<template v-slot:links>
<a href="">登陆</a>
<a href="">注册</a>
</template>
</Modal>
</teleport>
</template>
如此,我们便轻松地将组件挂载在任意我们想挂载的地方啦!