组件的嵌套
- 当我们用脚手架搭建了一个vue项目之后,可以简单看看它的结构,
src
中的main.js
是它的启动入口,文件内容如下
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
- 首先导入根组件(App组件),然后调用
createApp
函数创建Vue
的实例,最后调用mount
函数将页面渲染到index.html
文件中id="app"
的<div>
元素上,这一步是将Vue的元素app
挂载到DOM
上,app
元素对应的是App.vue
文件,这个文件内容如下。当项目运行起来之后,显示在页面上的就将会是这个文件的template
标签内的元素
<!--App.vue-->
<template>
<div>
<!-- 页面头部内容-->
<div class="header">
<h4>Header</h4>
<h4>NavBar</h4>
</div>
</div>
<div class="main">
<!-- 页面中间内容-->
<h4>Banner轮播图内容</h4>
<ul>
<li>商品信息1</li>
<li>商品信息2</li>
<li>商品信息3</li>
<li>商品信息4</li>
<li>商品信息5</li>
</ul>
</div>
<!-- 页面尾部内容-->
<div class="footer">
<h4>Footer</h4>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
}
</script>
<style>
.header, .main, .footer{
border: 1px solid#999;
margin-bottom: 4px;
}
</style>
- 上面代码将所有逻辑都放到了根组件中,虽然可以实现功能,但是不符合组件化开发的思想,我们应该把大的组件进行拆分,拆成一个个具有独立功能的小组件,然后将这些组件组合或嵌套在一起,构成应用程序。如下图所示
- 重构后的代码结构如下
<!--Header.vue-->
<template>
<div>
<div class="header">
<h4>Header</h4>
<h4>NavBar</h4>
</div>
</div>
</template>
<style scoped>
.header{
border: 1px solid#999;
margin-bottom: 4px;
}
</style>
<!--Footer.vue-->
<template>
<div class="footer">
<h4>Footer</h4>
</div>
</template>
<style scoped>
.footer {
border: 1px solid#999;
margin-bottom: 4px;
}
</style>
<!--Main.vue-->
<template>
<div class="main">
<main-banner></main-banner>
<main-product-list></main-product-list>
</div>
</template>
<style scoped>
.main {
border: 1px solid #999;
margin-bottom: 4px;
}
</style>
<script setup lang="ts">
import MainBanner from "@/chapters/chapter02/MainBanner.vue";
import MainProductList from "@/chapters/chapter02/MainProductList.vue";
</script>
<!--MainBanner.vue-->
<template>
<h4>Banner轮播图内容</h4>
</template>
<style scoped>
</style>
<!--MainProductList.vue-->
<template>
<ul>
<li>商品信息1</li>
<li>商品信息2</li>
<li>商品信息3</li>
<li>商品信息4</li>
<li>商品信息5</li>
</ul>
</template>
<style scoped>
</style>
<script setup lang="ts">
</script>
<!--App.vue-->
<template>
<div class="app">
<Header></Header>
<Main></Main>
<Footer></Footer>
</div>
</template>
<style scoped>
</style>
<script setup lang="ts">
import Header from "@/chapters/chapter02/Header.vue";
import Footer from "@/chapters/chapter02/Footer.vue";
import Main from "@/chapters/chapter02/Main.vue";
</script>
- 在
main.js
中引入上面App
组件,如下
// main.js
import { createApp } from 'vue'
import App from './chapters/chapter02/App.vue'
// import App from './App.vue'
createApp(App).mount('#app')
- 运行可以发现,重构后的代码效果与之前是相同的
组件样式
- 有几种用于编写单文件组件的样式,下面分别用例子演示
Scoped CSS
<style scoped>
是Vue.js 3中的一个样式作用域标记。在一个带有scoped
标记的<style>
标签中定义的样式仅在当前组件中生效,并且Vue.js 3会自动将选择器变异成带有唯一属性的选择器,以免和其他组件中的样式冲突。通常,我们将其称为组件的局部样式- 当单文件组件(SFC)的
style
标签带有scope属性时,表示在该标签中编写的样式都是局部样式,它的CSS只会应用到当前组件的元素上。这些CSS最终会被PostCSS转换,如下图
- 上面data-*是HTML5提供的数据属性特性,具体可查下HTML5文档了解
局部样式的泄露
看下面这个例子
<template>
<h4>App Title</h4>
<HelloWorld></HelloWorld>
</template>
<script setup lang="ts">
import HelloWorld from "@/chapters/chapter03/HelloWorld.vue";
</script>
<style scoped>
h4{
text-decoration: underline;
}
</style>
<template>
<h4>Hello World!</h4>
</template>
<style scoped>
</style>
- 实现的效果如下图所示
- 也就是说,
h4
样式从父组件中泄漏到子组件HelloWorld
中了。为了解决这个问题,有一些办法,最简单的办法是减少标签选择器的使用,转而使用class选择器,保证class选择器的唯一性,比如下面这样
<template>
<h4 class="hello-world">App Title</h4>
<HelloWorld></HelloWorld>
</template>
<script setup lang="ts">
import HelloWorld from "@/chapters/chapter03/HelloWorld.vue";
</script>
<style scoped>
.hello-world{
text-decoration: underline;
}
</style>
- 可以看到这样写父组件的样式没有泄露到子组件中,因为我们使用了class选择器
深度选择器
- 有时我们需要通过修改父组件的局部样式中修改子组件中某个元素的样式,这时,我们可以使用深度选择器
:deep()
这个伪类来实现,下面展示:deep()
的使用 - 我们把上面的文件改成下面这样
<template>
<h4>Hello World!</h4>
<h4 class="msg">My message</h4>
</template>
<style scoped>
.msg {
}
</style>
- 现在的效果如下
- 我们来通过
:deep()
来通过父组件来修改子组件的class
选择器msg
,如下
<template>
<div class="hello-world">
<h4>App Title</h4>
<HelloWorld></HelloWorld>
</div>
</template>
<script setup lang="ts">
import HelloWorld from "@/chapters/chapter03/HelloWorld.vue";
</script>
<style scoped>
:deep(.msg) {
text-decoration: underline;
}
</style>
<template>
<h4>Hello World!</h4>
<h4 class="msg">My message</h4>
</template>
<style scoped>
</style>
- 现在的效果如下