(06)首页开发——① Header 组件 | Vue.js 项目实战: 移动端“旅游网站”开发
转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
涉及面试题:
1. 何时需要一个单独的根元素?
2. 什么是 Asset URL 转换规则?
3. 是否可能将局部和全局样式混在一起?
4. 在 scoped CSS 中父级样式是否泄漏到子组件中?
编号:[vue_06]
1 首页 Header 组件的“结构”与“样式”
在我们的项目中,将利用 Stylus 编写 CSS,它可以让我们使用变量或者“mixin”之类的来辅助我们编写样式代码。使用 Stylus,首先需要在项目中安装。
1️⃣打开终端,在项目目录下安装 stylus
和 stylus-loader
:
npm install --save stylus stylus-loader
1️⃣-①:查看设计稿,设计稿宽度为 750px,是以 iPhone6 为原型的 2 倍设计稿。测量 Header,它的高度大概在 86px,左边黄框宽度 60px,右边的宽度 128px(主要学习 Vue 的内容,所以 CSS 部分不是足够精细,大家后续可根据官网尺寸自行调整);
2️⃣打开项目,在 src 的 pages 下 home 目录中新建一个 components 文件夹,这里面用来放组合首页所需的小组件。并在 components 中创建一个 Header.vue
,它是首页的头部组件:
<!-- ❗️复制一份 Home.vue 的代码到 Header.vue。 -->
<template>
<div>
this is HomeHeader. <!-- 2️⃣-①:更改内容以区分组件; -->
</div>
</template>
<script>
export default {
name: 'HomeHeader' // 2️⃣-②:组件名改为 HomeHeader。
}
</script>
<style scoped>
</style>
3️⃣打开 Home.vue
,在这里边使用 Header.vue
组件:
<template>
<div>
<home-header></home-header> <!-- 3️⃣-③:使用 HomeHeader 组件。 -->
</div>
</template>
<script>
import HomeHeader from './components/Header' /*
3️⃣-①:从当前目录下的 components
中引入 Header.vue;
*/
export default {
name: 'Home',
components: { // 3️⃣-②:注册局部组件 HomeHeader;
HomeHeader
}
}
</script>
<style scoped>
</style>
保存后,刷新页面查看效果, Header.vue
的内容正确显示在首页上,且无报错:
4️⃣根据设计稿来看, Header.vue
部分大概可以拆分成左、中、右 3 部分。而知道了 Header 的整体布局内容,可以编写这部分的代码了:
<template>
<div class="header"> <!-- 4️⃣-⑤:给最外层包裹的 div 添加 class 为 header; -->
<!-- 4️⃣-①:在 div 中添加三个 div,分别为 header 的三部分; -->
<div class="header-left"></div> <!-- 4️⃣-②:第一个为左边 icon 部分; -->
<div class="header-input"></div> <!-- 4️⃣-③:第二个为中间输入框部分; -->
<div class="header-right"></div> <!-- 4️⃣-④:第三个为右边城市选择部分; -->
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<!-- 4️⃣-⑥:添加 lang="stylus",表示使用 stylus 编写 CSS; -->
<!-- 4️⃣-⑦:添加 scoped,让样式只在本组件内起作用; -->
<style lang="stylus" scoped>
</style>
Stylus 编写 CSS 🆚普通 CSS 写法(这里简单了解,更多内容将在实际代码编写中有所体会):
/* 普通 CSS 写法: */
.header {
color: red;
}
.header .header-left {
float: left;
}
/* Stylus 的写法经过了简化,写法为: */
.header
color: red
.header-left /* ❗️缩进一个单位,表示 header 下的 header-left。 */
float: left
编写 Header.vue
的样式中,我们使用“rem”这个单位。打开 reset.css
可以看到,我们给根元素设置的字体大小为 50px
。所以 2 倍设计稿中的 86px,实际尺寸应为 43px,换算为 rem,即: 43/50=0.86
。
5️⃣确定了根元素大小后,再继续编写样式:
<template>
<div class="header">
<!-- 5️⃣-⑥:添加文字内容以方便查看效果。 -->
<div class="header-left">返回</div>
<div class="header-input">搜索</div>
<div class="header-right">城市</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
/* ❗️添加样式。 */
.header /* 5️⃣-④:设置 header 为 flex 布局; */
display: flex
line-height: .86rem /*
5️⃣-①:设置 header 行高 为 0.86 rem,设置字体颜色为白色,
背景色为 #00bcd4;
*/
color: #fff
background: #00bcd4
.header-left /* 5️⃣-②:设置 .header-left 左浮动,宽度 0.64rem; */
float: left
width: .64rem
.header-input /* 5️⃣-⑤:设置 header-input 的 flex 为 1,让它的内容自动撑开; */
flex: 1
.header-right /*
5️⃣-③:设置 header-right 右浮动,最小宽度为 1.04rem,左右 padding 为 0.1rem,
内容居中;
*/
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
</style>
保存后,返回页面查看一下,Header 部分的样式有了,”城市“部分的内容基本完毕:
6️⃣继续完善”返回“部分和”搜索“输入框部分的内容:
<template>
<div class="header">
<div class="header-left">返回</div>
<div class="header-input">输入城市/景点/游玩主题</div> <!-- 6️⃣-⑤:修改文字内容。 -->
<div class="header-right">城市</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
.header
display: flex
line-height: .86rem
color: #fff
background: #00bcd4
.header-left
float: left
width: .64rem
.header-input
flex: 1
margin-top: .12rem /* 6️⃣-②:添加“上”、“左” margin; */
margin-left: .2rem
height: .64rem /* 6️⃣-④:设置高度和 line-height 为 0.64rem; */
line-height: .64rem
color: #ccc /* 6️⃣-③:添加字体颜色 #ccc; */
background: #fff /* 6️⃣-①:设置 input 框部分的背景色为白色,添加圆角 0.1rem; */
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
</style>
保存后,返回页面查看效果。现在与设计稿上不同的地方在于三个细节点:“返回”图标、 输入城市/景点/游玩主题
前面的“搜索”图标、 城市
后面的“下拉三角”图标。
2 引入 iconfont
7️⃣打开 iconfont 官网,进入“图标库”下的“官方图标库”(本项目中使用 Unicode 码的方式):
7️⃣-①:找到并打开“阿里云官网”图标库;
7️⃣-②:找到我们项目需要的所有图标(除了 搜索
、 返回
、 下拉三角
以外,后面还会需要一个“图片”图标,我们在这里一次添加上),将图标添加进“购物车”后,**加入到新项目“qdywxs-travel”**中,点击确定;
7️⃣-③:打开“图标管理”下的“我的项目”,找到我们的项目“qdywxs-travel”,选择将图标的** Unicode** 下载至本地;

7️⃣-④:下载完成后,打开解压后的文件夹,可以看到里面有一些 .css
文件、 .js
文件和一些字体文件;
7️⃣-⑤:把我们所需要的文件 iconfont.css
放入 src 下的 aseets 中的 styles 文件夹内,并在 styles 中新建 iconfont 文件夹,放入 iconfont.eot
、 iconfont.svg
、 iconfont.ttf
、 iconfont.woff
四个文件;
现在的项目结构:
7️⃣-⑥:在编辑器中打开 iconfont.css
,由于 url 中的地址不再是“当前目录下的 xxx
文件”了,所以需要改为“当前目录下的 iconfont 文件夹中的 xxx
文件”;
@font-face {font-family: "iconfont";
/* ❗️在原本的 url 前面加上 ./iconfont/ ; */
src: url('./iconfont/iconfont.eot?t=1568633121681'); /* IE9 */
src: url('./iconfont/iconfont.eot?t=1568633121681#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAANMAAsAAAAAB6gAAAL/AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDMgqCNIIlATYCJAMUCwwABCAFhG0HWhvDBhHVmzfIviqw2xwvTJAMLtiwLeA7CCfRNu++qbmJg/1+EcHT2m/e7t59waRBEo10j5q844nqlQSxUZImmpfAj/7lu+QKYsIEOv+X1G6uXE+Y2hHa2jkoeh0uIgAif3MzdI2KMGzHefC/PCNdI82vi7VVMStBt12D6dRoKpEOiSGRSRoSjSamL5rCfyhfav9h7KqlOKjDBNpGueGsRicSUFfgUYE4r64sBvVMSFHUbFOoKxYW8Qijme4RDzwEvx9/7qmTVBmetnUYVQahn4FveXiIDgkUiKdzQF1GxhxQiItK1zkcpeZgbbs71yLQ1pSkqdrFv2W95VEKiQR7Qpv8wyMkWSFqkKsTLEoXPgMZQvAZj5D4zELIfOZJPsxBFWijhTXgBqKNuTyphOjsV1OrWq06HyiVmXI5I5MRhaKZvVdyfAWbEuQnzipDmF6W5BCsV7pjhyGvpqJb96mRmYOhw9nj4SNyJxmWzoqHRI0VlUGEaSXs/T2qW1ysQ4AvylBd0vnZVSBwca7wRZM3N5NcjpAZTSK/b4XzVnUCPNO5xXoKTO8+BJN/gwOU5vZKegDwca9rYGVobWhloDM4xKSTACQemyIy/4MK9Fu6AP73JAbId5gD6RJtArL0h/7Gc0cSr1Av8P9pFfC+u+IQhP4PDEs3JTqL/4e4Y1FxpNhcFSufiSxPZfxOVMmuauMv+Pq9TqMZ6pwgNDNiFZKGEciaxtAFPweVDvNQa1qAtlkxyzsMaIFFaWHGOIDQ6xySbk+Q9eKjC/4FKsN+odYbhrbtMNqww0SsERGEO+MFxx/MmaKkVhZh7g3V2++Ulgb0F1I1URi7IZ/dMCJNMaV+1MQsQVIKsILj0PsEmZLFkzvNnJe+l1Uf1J0pNEIE4c54wfEHc6YoRWUWhZ+/oXr7nRoK2pwvpGpah7EbOqA3VexUcCtPqR81MUuQlAKsoB96nyBX97N4cqcHJPLSk1Kyq6pbXhPecRN8uPfEaneaLo2LD7eM9bFEUdGo8EzOuWkAAA==') format('woff2'),
/*❗️在原本的 url 前面加上 ./iconfont/ ;*/
url('./iconfont/iconfont.woff?t=1568633121681') format('woff'),
url('./iconfont/iconfont.ttf?t=1568633121681') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./iconfont/iconfont.svg?t=1568633121681#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ❗️我们将使用 Unicode 码,所以删除这部分无用代码。 */
/*
.icon-icon-test:before {
content: "\e63c";
}
.icon-icon-test1:before {
content: "\e64a";
}
.icon-icon-test2:before {
content: "\e658";
}
.icon-icon-test3:before {
content: "\e65c";
}
*/
7️⃣-⑦:因为各个页面基本都需要使用 iconfont,所以我们打开 src 下的 main.js
,在这里边引入 iconfont(保存后,刷新页面查看控制台,没有报错即引入成功);
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import fastClick from 'fastclick'
import './assets/styles/reset.css'
import './assets/styles/border.css'
// ❗️引入 iconfont。
import './assets/styles/iconfont.css'
Vue.config.productionTip = false
fastClick.attach(document.body)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
7️⃣-⑧:在首页 Header.vue
中使用 iconfont;
<template>
<div class="header">
<!-- ❗在三个需要图标的位置,各添加一个 span 标签,并增加 iconfont 类名。 -->
<div class="header-left">
<!-- ❗️这里是一个图标,所以删除“返回”文字; -->
<span class="iconfont"></span>
</div>
<div class="header-input">
<!-- ❗️图标在文字之前; -->
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
<!-- ❗️图标在文字之后。 -->
城市
<span class="iconfont"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
.header
display: flex
line-height: .86rem
color: #fff
background: #00bcd4
.header-left
float: left
width: .64rem
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
</style>
7️⃣-⑨:在 iconfont 官网我们的项目图标处,分别复制各个图标的代码(即 Unicode 码)放入 span 标签内。
<template>
<div class="header">
<div class="header-left">
<!-- ❗️粘贴“返回”图标的 Unicode 码; -->
<span class="iconfont"></span>
</div>
<div class="header-input">
<!-- ❗️粘贴“搜索”图标的 Unicode 码; -->
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
城市
<!-- ❗️粘贴“下拉三角”图标的 Unicode 码。 -->
<span class="iconfont"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
.header
display: flex
line-height: .86rem
color: #fff
background: #00bcd4
.header-left
float: left
width: .64rem
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
</style>
保存后,刷新页面查看,图标已经出现在了页面上,只是图标的细节(大小、位置等)还需要再调整一下:
8️⃣返回 Header.vue
,对图标样式进行微调:
<template>
<div class="header">
<div class="header-left">
<span class="iconfont back-icon"></span> <!-- 8️⃣-①:添加类名 back-icon; -->
</div>
<div class="header-input">
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
城市
<span class="iconfont arrow-icon"></span> <!-- 8️⃣-⑦:给下拉图标增加类名
arrow-icon; -->
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
.header
display: flex
line-height: .86rem
color: #fff
background: #00bcd4
.header-left
float: left
width: .64rem
.back-icon /* 8️⃣-②:在 header-left 下编写 back-icon 的样式; */
display: block /* 8️⃣-③:display: block 让它撑满左边的区域; */
text-align: center /* 8️⃣-④:让图标居中; */
font-size: .56rem /*8️⃣-⑤:将返回图标调整大一些;*/
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
padding-left: .12rem /* 8️⃣-⑥:给 header-input 添加 padding-left; */
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
.arrow-icon
margin-left: -0.1rem /*
8️⃣-⑧:给 header-right 下的 arrow-icon 添加负 margin-left,
缩小图标与“城市”之间的距离。
*/
</style>
保存后,刷新页面查看:
3 代码优化
3.1 定义样式的“变量”
完成了 Header 组件的布局,还有一个细节我们可以注意到:Header 的背景色,同时是整个项目的主题色。也就是说,其他很多地方,我们也会用到这个颜色。
所以,我们可以把这个颜色单独放在一个变量里,当其他地方需要使用时,可以直接引用。当未来项目的主题色、风格等需要切换时,只用改动这个变量,整个项目中应用到的地方都会随之更改,这大大提升了项目的可维护性。
9️⃣在 styles 中新建一个放“全局变量”的文件 varibles.styl
( .styl
后缀表示这是一个 stylus 文件):
// ❗️添加通用背景色。
$bgColor = #00bcd4
9️⃣-①:在 Header.vue
中使用变量;
<template>
<div class="header">
<div class="header-left">
<span class="iconfont back-icon"></span>
</div>
<div class="header-input">
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
城市
<span class="iconfont arrow-icon"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
/* ❗️样式中引入样式,import 前要加 @ 符号。 */
@import '~@/assets/styles/varibles.styl' /*
9️⃣-②:从 src 下的 assets 中的 styles
内引入 varibles.styl;
❗️在 CSS 中使用 @ 符号引入其他地方的 CSS,
需要在 @ 前面加上 ~ 符。
*/
.header
display: flex
line-height: .86rem
color: #fff
background: $bgColor /* 9️⃣-③:background 对应的颜色替换为 $bgColor 变量。 */
.header-left
float: left
width: .64rem
.back-icon
display: block
text-align: center
font-size: .56rem
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
padding-left: .12rem
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
.arrow-icon
margin-left: -0.1rem
</style>
保存后,返回页面可以看到,没有报错,样式正常。顺便再做一个小测试,看看更改了全局变量后,样式是否会发生变化:
3.2 配置路径的“别名”
在上面引入 varibles.styl
时可以发现,我们写入的路径还是比较长。而 styles 这个目录下有许多样式相关的文件,这个路径我们还会使用得比较频繁。
❓那么,既然 src 目录可以用 @
符号来表示,那么我们是否也可以给 styles
的路径设置得更为方便呢?
答:可以。
🔟在 bulid 目录下的 webpack.base.conf.js
中,找到 resolve
下的 alias
。可以看到, src 目录也是在这里定义了“别名” @
。
所以,我们在它的下方也“依葫芦画瓢“,把 styles
定义为路径 src/assets/styles
的别名:
🔟-①:打开 Header.vue
,引入路径直接使用 styles
;
<template>
<div class="header">
<div class="header-left">
<span class="iconfont back-icon"></span>
</div>
<div class="header-input">
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
城市
<span class="iconfont arrow-icon"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<style lang="stylus" scoped>
/* ❗️styles 这个别名前面��然要加上 ~ 符号。 */
@import '~styles/varibles.styl'
.header
display: flex
line-height: .86rem
color: #fff
background: $bgColor
.header-left
float: left
width: .64rem
.back-icon
display: block
text-align: center
font-size: .56rem
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
padding-left: .12rem
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
min-width: 1.04rem
padding: 0 .1rem
text-align: center
.arrow-icon
margin-left: -0.1rem
</style>
🔟-②:打开 src 下的 main.js
,这里边引入 CSS 文件的位置也可以直接改为 styles
。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import fastClick from 'fastclick'
/* ❗️路径改为 styles 下的 .css 文件。 */
import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'
Vue.config.productionTip = false
fastClick.attach(document.body)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
❓更改完毕,保存代码后,为什么项目一直报错? 答:这是因为我们对 Webpack 配置进行了更改。如果在项目中更改了 Webpack 的配置项,那么一定要重启服务器。
运行服务器的终端中, control+C
关闭正在运行的服务器,运行命令 npm run dev
重启项目可以看到对代码优化后的页面效果正常:
🏆本篇,我们主要使用了两个新内容,从而简化了代码,让我们的代码更为整洁:
- 利用 Stylus 编写 CSS 样式,并定义、使用了变量;
- 通过 Webpack 的配置项定义了路径的别名。
祝好,qdywxs ♥ you!
转载自:https://juejin.cn/post/7387676645979144244