在上一篇文章中,我们创建了一个项目,也探究了一些基础文件的作用。这次我们要探究Vue中的App.vue文件,以及其他.vue文件的作用。
组件
每个Vue项目都是由若干个.vue文件,也就是“组件”组成的。组件和HTML中的元素类似,但是不光有画面UI上的功能,也承担的一定的代码和运算任务。我们以App.vue(这是所有Vue项目的入口文件)的代码为例,看一下一个组件的大致结构。
// 脚本部分,也就是JavaScript代码
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
// 页面部分,也就是HTML代码
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
// 样式表部分,也就是css代码
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
可以看到,前端的三件套的三部分分离的都很好,放入了一个文件中,经过Vue的处理后能够实现所有的功能。这被称作单文件组件(SFC)。Vue处理后,每个组件都会变成一个JavaScript对象,如下所示:
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
// 或者 `template: '#my-template-element'`
}
组件的引入和嵌套
我们观察上文的template中的代码,会发现有一些HTML元素并不是原生的元素,例如<HelloWorld msg="You did it!" />和<TheWelcome />,观察components文件夹,会发现里面也有对应的HelloWorld.vue文件和TheWelcome.vue文件。是不是组件发生了嵌套?确实是这样的。在Vue中一个组件可以嵌套另一个组件,以实现复杂的功能。Vue项目中的组件们会以“组件树”的形式被渲染,类似于DOM树。
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
Vue使用了ES6中的模块化特性来实现组件的导入和导出。我们可以尝试把<Helloworld>元素和对应的import语句删除,会发现页面上已经看不到了。
组件的参数传递
既然组件是可以嵌套的,那么我们能不能在父组件中传递一些参数反应在子组件中呢?当然是可以的。这被称为组件的参数传递。我们先新建一个ArticlePreview.vue组件来进行测试。这个组件担任着在博客列表中预览博客标题和正文的功能。
<script setup>
defineProps(['title','mainText']);
</script>
<template>
<div>
<!-- 这里的语法可以让HTML显示script中的数据 -->
{{ title }}
{{ mainText }}
</div>
</template>
<style>
</style>
defineProps是Vue3中预编译的宏指令,不需要引入,当Vue处理组件的时候,所有的参数都会被包装成一个对象(作为函数的返回值)绑定到组件中。
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<ArticlePreview title="1" main-text="111"/>
<ArticlePreview title="2" main-text="222"/>
<div class="wrapper">
</div>
</header>
在网页中就可以看到文章的标题和正文。
组件插槽
我们都知道元素不仅仅都是以单个HTML标签存在的,还有很多是以成对的元素存在的(比如<div>和</div>)。我们是不是也可以在自定义组件中实现这种效果呢?答案当然是可以的。这就叫做组件的插槽(slots)。
比如我们想让博客预览组件中的插槽代表博客标题,而不使用组件的属性传递,那么就可以直接用<slot/>元素来创造一个占位符。
<script setup>
defineProps(['mainText']);
</script>
<template>
<div>
<slot/>
{{ mainText }}
</div>
</template>
<style>
</style>
在App.vue中可以像下文这样使用它,就能达到我们需要的效果了。
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<ArticlePreview main-text="111">1</ArticlePreview>
<ArticlePreview main-text="222">2</ArticlePreview>
<div class="wrapper">
</div>
</header>
组件插槽和传参的区别是显而易见的。插槽使用的是占位符,因此传入的值是很难作为JavaScript的数据来使用的,而且插槽是不能使用多个占位符的,毕竟两个元素之间只能夹一个数据。
<template>
<div>
<!-- 这样就会报错 -->
<slot/>
1
<slot/>
{{ mainText }}
</div>
</template>