(超级详细)Vue项目实战App2 - - -主页
一、项目环境准备1.Node.jsa. 官网下载安装包:Node.js中文官网b. 安装检测:Windows系统打开cmd命令行, Mac系统打开终端node -v# 输入后回车v14.15.1# 出现版本号, 则表示'Node.js'安装成功npm -v# 输入后回车(检测'Node.js'的包管理工具是否自动安装)6.14.8# 出现版本号, 说明安装成功2.Vue脚手架( Vue-CLI )
·
一、Home
1.安装stylus
cd Travel
# 进入项目目录文件
npm install stylus --save
# 安装stylus
npm install stylus-loader --save
# 安装stylus的依赖包
npm run dev
# 运行项目
2. 组件化开发
该页面可分为’Header’、‘Swiper’、‘Icons’、‘Recommend’、'Weekend’五个组件组成
3、定义全局变量、方法、目录别名
a.创建’varibles.styl’文件 (路径: src/assets/styles/varibles.styl)
// 主题色
$bgColor = #00bcd4
// 正文字体
$darkTextColor = #333
// header高度
$headerHight = .86rem
b.创建’mixins.styl’文件 (路径: src/assets/styles/mixins.styl)
// 实现文字溢出的方法
ellipsis()
overflow hidden
white-space nowrap
text-overflow ellipsis
二、iconfont
1.创建’Iconfont’文件夹(路径: src/assets/styles/iconfont)
2. 下载iconfont文件,将图标’下载至本地’
3. 迁移文件至iconfont文件夹
4. 编辑’iconfont.css’文件(路径: src/assets/styles/iconfont.css)
@font-face {font-family: "iconfont";
/* 字体文件不在当前目录下,所以需要修改路径 */
src: url('./iconfont/iconfont.eot?t=1610348313991'); /* IE9 */
src: url('./iconfont/iconfont.eot?t=1610348313991#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAQYAAsAAAAACHgAAAPKAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDSAqDaIM/ATYCJAMYCw4ABCAFhG0HWRt2B8gOJS3BwABgAABgBF/7ofvu7l8QHJLKxLMKq6joKAKVlEC1rk7WiNpqNiL/12Z+ZqqEqrNm0VPFhKRihAwJMfKGIVJRY2six+ZWUT3v/xwzXYrnX6VLNVUW7Y3G4wENLOOiPIruHeAdyPGB6KmA59KB7KDfNxNoN6oct91n0CiopfBWBeJMC8pQq6ZXRkpDq1B7DhbxFRqt6TO6C+BL+vPxH9xRi6TJvI1Hp70D0O2X++NpPKtqlpMYAdLtrFCOkbEMFOKx1/4A5/FlWLv1F6rZATq1Ssov94fxH+0fe348XVVFTeG5Q6cfJ0uP9Y/XEBUVd4I9JBp+uUMmmV/jmSR+25kEv3syKfw+3aYBdcPmdnwAfhB22Z4FUaVeQRgWvlbtF6buvQrp1ZX8uvuutcTW9aLYg1GN73nhvq/ZQ8m/ZM+mWZv32pYenn1o6ob8yfsOzGoizZ7NrV/fpMn6/c181mzlwQPvw4e++/ele/cyUVUI5ShcRt5g7d/bRFq/ac++pqkuAdLVGxmv33GvXOrmvXCPX6CRWBoJn9nT8PAJT6sHrTwnpB0e/gHv2VF362Wu6/yBjtAKrttZrseKgWWT1nPLhwmlp5udLhUapgpS4FrB350vkfNZwY+fPwH03+bmnrmz+eefX4Xw2HYEf/yIj+IjseqILCpFECLzaNdV+MYNHClXyiKIRSGUq1C/rP2WsqE3Mt2GJFGYFco29R6a/fQJ7l03UaWFTJwz9u2eg7/3Hyke3rLcRFYlMv1KyzI7jeHSaS7iHZtOjUFjUSpVMCaVhmnoWfINOgzIE6gBwPP1bOJuAKQ3aCMgH6Ib//zv+Park2dSvU7/a9kK+JnbvEXqf75eYFmiVYIt/Z+lzL5iyz1DLWbv8FIC1TwMEtr9If5dd78XW+mG2MWEVkMlJC1GIGs1jSzYZWh0WIWq1Rq0W9L3eIcBFBalDYvmCxB6HYek23fIemWRBfscGsO+Q9UbGNodR6MLdpgNje9JicxIQPRnRE3R42pgYnxWOoIEzahMq/IU5yXU0EKiy+5spvqSOKFbbDGSQTdjqqhSPSb2EW5HolFdTFA9TBRmDzGW6OBwqH2PZFf0GPC2UISMIQJEfhkijUIXp8ajGb71/SMQQaYoGR3paQl6EZRBm1zkYudkIPtq4qyeV5luSApyYxhVXKaidDGiPrASUT+uEyX654URCsYutCCa0IGDBqlcjf14XexbHoN23ofVSJGjRBM1WrReaUqFqkWj1dOaHJVrGLppmDovU6qnKqJEZTUMIlMlBAAAAA==') format('woff2'),
url('./iconfont/iconfont.woff?t=1610348313991') format('woff'),
url('./iconfont/iconfont.ttf?t=1610348313991') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./iconfont/iconfont.svg?t=1610348313991#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;
}
5. 引用’Iconfont’,编辑’main.js’
// 引用iconfont
import 'styles/iconfont.css'
三、Header
1. 新建’components’、'home’文件夹(路径: src/pages/home/components/Header.vue)
2.创建’Header.vue’文件
<template>
<div class="header">
Home
</div>
</template>
<script>
export default {
name: 'Header'
}
</script>
3. 编辑’Home.vue’文件 - - - 引用’Header.vue’组件
<template>
<div>
<home-header></home-header>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
export default {
name: 'Home',
components: {
HomeHeader
}
}
</script>
<style>
</style>
4. 编写’Header.vue’样式
<template>
<div class="header">
<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>
// 引用'varibles.styl'变量样式
@import '~styles/varibles.styl'
.header
display flex
// width 100%
// height $headerHight
background-color $bgColor
color #fff
text-align center
line-height $headerHight
.header-left
float left
width .64rem
.header-input
flex 1
margin-left .2rem
margin-top .12rem
padding-left .2rem
height .64rem
text-align left
line-height .64rem
color #ccc
border-radius .1rem
background #fff
.header-right
float right
width 1.24rem
padding 0 .1rem
</style>
5. 添加iconfont
<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>
// 引用'varibles.styl'变量样式
@import '~styles/varibles.styl'
.header
display flex
// width 100%
height $headerHight
background-color $bgColor
color #fff
text-align center
line-height $headerHight
.header-left
float left
width .64rem
.back-icon
font-size .6rem
.header-input
flex 1
margin-left .2rem
margin-top .12rem
padding-left .2rem
height .64rem
text-align left
line-height .64rem
color #ccc
border-radius .1rem
background #fff
.header-right
float right
width 1.24rem
padding 0 .1rem
.arrow-icon
margin-left -.1rem
font-size .4rem
</style>
四、Swiper
1. 创建’Swiper.vue’文件
<template>
<div class="Swiper">
Swiper
</div>
</template>
<script>
export default {
name: 'HomeSwiper'
}
</script>
<style scoped>
</style>
2. 引用’Swiper.vue’组件 - - - 编辑’Home.vue’文件
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper
}
}
</script>
<style>
</style>
3. 安装轮播图插件
// 当前项目目录安装
npm install vue-awesome-swiper@2.6.7 --save
# 安装的2.6.7版本
4. 导入轮播图插件 - - - 编辑’main.js’(路径: src/main.js)
// 引用轮播图插件
import VueAwesomeSwiper from 'vue-awesome-swiper'
// 引用轮播图插件的css样式
import 'swiper/dist/css/swiper.css'
// 用轮播图插件
Vue.use(VueAwesomeSwiper)
5. 编写’Swiper.vue’样式
<template>
<swiper :options="swiperOption">
<!-- slides -->
<swiper-slide>I'm Slide 1</swiper-slide>
<swiper-slide>I'm Slide 2</swiper-slide>
<swiper-slide>I'm Slide 3</swiper-slide>
<!-- Optional controls -->
<div class="swiper-pagination" slot="pagination"></div>
<div class="swiper-button-prev" slot="button-prev"></div>
<div class="swiper-button-next" slot="button-next"></div>
<div class="swiper-scrollbar" slot="scrollbar"></div>
</swiper>
</template>
<script>
export default {
name: 'HomeSwiper',
data() {
return {
swiperOption: {}
}
}
}
</script>
<style lang="stylus" scoped>
</style>
6. 完善’Swiper.vue’样式 - - - 图片防抖
<template>
<div class="wrapper">
<swiper :options="swiperOption">
<swiper-slide>
<img class='swiper-img' src="http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img class='swiper-img' src="http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg" alt="">
</swiper-slide>
<!-- 分页 -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
data () {
return {
swiperOption: {}
}
}
}
</script>
<style lang="stylus" scoped>
.wrapper
height 0
padding-bottom 31.25%
overflow hidden
background #fff
.swiper-img
width 100%
</style>
7. 完善’Swiper.vue’样式 - - - 分页样式
<template>
<div class="wrapper">
<swiper :options="swiperOption">
<swiper-slide>
<img class='swiper-img' src="http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img class='swiper-img' src="http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg" alt="">
</swiper-slide>
<!-- 分页 -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
data () {
return {
swiperOption: {
// 填入一个配置项
pagination: '.swiper-pagination'
}
}
}
}
</script>
<style lang="stylus" scoped>
// 通过鼠标右键审查元素定位要'class',从而修改样式(stylus的样式穿透)
.wrapper >>> .swiper-pagination-bullet-active
background #fff
// background #fff !important
.wrapper
height 0
padding-bottom 31.25%
overflow hidden
background #fff
.swiper-img
width 100%
</style>
8. 完善’Swiper.vue’样式 - - - banner图循环输出
<template>
<div class="wrapper">
<swiper :options="swiperOption">
<swiper-slide v-for="item of swiperList" :key="item.id">
<img class='swiper-img' :src="item.imgUrl" alt="">
</swiper-slide>
<!-- 分页 -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
data () {
return {
swiperOption: {
// 填入一个配置项
pagination: '.swiper-pagination',
// 循环轮播
loop: true
},
swiperList: [{
id: '001',
imgUrl: 'http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg'
},
{
id: '002',
imgUrl: 'http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg'
}]
}
}
}
</script>
<style lang="stylus" scoped>
// 通过鼠标右键审查元素定位要'class',从而修改样式(stylus的样式穿透)
.wrapper >>> .swiper-pagination-bullet-active
background #fff
// background #fff !important
.wrapper
height 0
padding-bottom 31.25%
overflow hidden
background #fff
.swiper-img
width 100%
</style>
五、Icons
1. 创建’Icons.vue’文件
<template>
<div>Icon</div>
</template>
<script>
export default {
name: 'HomeIcon'
}
</script>
<style lang="stylus" scoped>
</style>
2. 引用’Icons.vue’组件 - - - 编辑’Home.vue’文件
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icon></home-icon>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcon from './components/Icon'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcon
}
}
</script>
<style>
</style>
3. 编辑’Icons.vue’文件 - - - 实现基本布局
<template>
<div class="icons">
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="https://s.qunarzz.com/homenode/images/touchheader/piao.png" alt="">
</div>
<p class="icon-desc">特色景点</p>
</div>
</div>
</template>
<script>
export default {
name: 'HomeIcons'
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
.icons
height 0
padding-bottom 50%
background #fff
.icon
position relative
float left
width 25%
height 0
padding-bottom 25%
// background skyblue
overflow hidden
.icon-img
position absolute
left 0
top 0
right 0
bottom .44rem
padding .1rem
box-sizing border-box
.icon-img-content
display block
height 100%
margin 0 auto
.icon-desc
position absolute
left 0
right 0
bottom 0
height .44rem
line-height .44rem
text-align center
color $darkTextColor
</style>
4. 完善’Icons.vue’文件 - - - 双向绑定内容
<template>
<div class="icons">
<swiper :options="swiperOption">
<swiper-slide>
<div class="icon" v-for="item of iconList" :key="item.id">
<div class="icon-img">
<img class="icon-img-content" :src="item.imgUrl" alt="">
</div>
<p class="icon-desc">{{item.desc}}</p>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeIcons',
// data () {
// return {
// swiperOption: {
// autoplay: false
// }
// }
// },
data () {
return {
iconList: [{
id: '0001',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/piao.png',
desc: '景点门票'
},
{
id: '0002',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/package.png',
desc: '度假'
},
{
id: '0003',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/train.png',
desc: '火车票'
},
{
id: '0004',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/flight.png',
desc: '机票'
},
{
id: '0005',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/hotel.png',
desc: '酒店'
},
{
id: '0006',
imgUrl: 'https://picbed.qunarzz.com/f5e5770393d759578962e53ee67798c8.png',
desc: '海外酒店'
},
{
id: '0007',
imgUrl: 'https://picbed.qunarzz.com/a36d2288f19e54562338f4d8ef986288.png',
desc: '特价机票'
},
{
id: '0008',
imgUrl: 'https://picbed.qunarzz.com/1316dc82d1ce6259686d5a68880e5a9d.png',
desc: '攻略'
},
{
id: '0009',
imgUrl: 'https://picbed.qunarzz.com/ae617a31e0bd5803d76918b817f6d942.png',
desc: '自由行'
},
{
id: '0010',
imgUrl: 'https://picbed.qunarzz.com/377db8cb2143aebf01869c9baad3d325.png',
desc: '汽车票'
}]
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
// 右键审查元素定位到'class',从而修改样式
.icons >>> .swiper-container
// swiper自带overflow属性
// overflow hidden
height 0
padding-bottom 50%
// background #f5f5f5
.icon
position relative
float left
width 25%
height 0
padding-bottom 25%
// background skyblue
overflow hidden
.icon-img
position absolute
left 0
top 0
right 0
bottom .44rem
padding .1rem
box-sizing border-box
.icon-img-content
display block
height 100%
margin 0 auto
.icon-desc
position absolute
left 0
right 0
bottom 0
height .44rem
line-height .44rem
text-align center
color $darkTextColor
</style>
5. 完善’Icons.vue’文件 - - - 实现分页计算
<template>
<div class="icons">
<swiper :options="swiperOption">
<swiper-slide v-for="(page, index) of pages" :key="index">
<div class="icon" v-for="item of page" :key="item.id">
<div class="icon-img">
<img class="icon-img-content" :src="item.imgUrl" alt="">
</div>
<p class="icon-desc">{{item.desc}}</p>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeIcons',
data () {
return {
swiperOption: {
// 防止轮播自动播放
autoplay: false
},
iconList: [{
id: '0001',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/piao.png',
desc: '景点门票'
},
{
id: '0002',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/package.png',
desc: '度假'
},
{
id: '0003',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/train.png',
desc: '火车票'
},
{
id: '0004',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/flight.png',
desc: '机票'
},
{
id: '0005',
imgUrl: 'https://s.qunarzz.com/homenode/images/touchheader/hotel.png',
desc: '酒店'
},
{
id: '0006',
imgUrl: 'https://picbed.qunarzz.com/f5e5770393d759578962e53ee67798c8.png',
desc: '海外酒店'
},
{
id: '0007',
imgUrl: 'https://picbed.qunarzz.com/a36d2288f19e54562338f4d8ef986288.png',
desc: '特价机票'
},
{
id: '0008',
imgUrl: 'https://picbed.qunarzz.com/1316dc82d1ce6259686d5a68880e5a9d.png',
desc: '攻略'
},
{
id: '0009',
imgUrl: 'https://picbed.qunarzz.com/ae617a31e0bd5803d76918b817f6d942.png',
desc: '自由行'
},
{
id: '0010',
imgUrl: 'https://picbed.qunarzz.com/377db8cb2143aebf01869c9baad3d325.png',
desc: '汽车票'
}]
}
},
// 计算
computed: {
// 定义一个函数
pages () {
// 申明一个空数组,作为传递所有item后总共的页数
const pages = []
// 将iconList循环一遍,可以获取到两个数据'循环项'和它对应的'下标'
this.iconList.forEach((item, index) => {
// 定义一个'page',计算页数 page = index/8
const page = Math.floor(index / 8)
// page = index / 8 < 1时,pages[0]不变,不执行条件
// page = index / 8 >= 1时,pages[page]和pages[0]不一致,执行条件
if(!pages[page]) {
// 重新创建一个空数组,然后都把新数据添加至空数组当前页
pages[page] = []
}
pages[page].push(item)
})
return pages
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
// 右键审查元素定位到'class',从而修改样式
.icons >>> .swiper-container
// swiper自带overflow属性
// overflow hidden
height 0
padding-bottom 50%
// background #f5f5f5
.icon
position relative
float left
width 25%
height 0
padding-bottom 25%
// background skyblue
overflow hidden
.icon-img
position absolute
left 0
top 0
right 0
bottom .44rem
padding .1rem
box-sizing border-box
.icon-img-content
display block
height 100%
margin 0 auto
.icon-desc
position absolute
left 0
right 0
bottom 0
height .44rem
line-height .44rem
text-align center
color $darkTextColor
// 封装的文字溢出效果
ellipsis()
</style>
六、Recommend
1. 创建’Recommend.vue’文件
<template>
<div>
Recommend
</div>
</template>
<script>
export default {
name: 'HomeRecommend'
}
</script>
<style lang="stylus" scoped>
</style>
2. 引用’Recommend.vue’组件
编辑’Home.vue’文件
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend
}
}
</script>
<style>
</style>
3. 编辑’Recommend.vue’ - - - 实现基本布局
<template>
<div>
<div class="title">热销推荐</div>
<div class="item">
<img class="item-img" src="https://imgs.qunarzz.com/vs_ceph_vs_tts/9608dc40-1209-4b9d-a73f-1030d1f351fe.gif">
<div class="item-info">
<p class="item-title">故宫</p>
<p class="item-desc">东方建筑代表,世界宫殿典范</p>
<button class="item-button">查看详情</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'HomeRecommend'
}
</script>
<style lang="stylus" scoped>
.title
margin-top .2rem
line-height .8rem
background #eee
text-indent .2rem
.item
overflow hidden
display flex
height 1.9rem
background #f5f5f5
.item-img
width 1.7rem
height 1.7rem
margin .1rem
.item-info
flex 1
padding .1rem
// 限定最小宽度,实现文字溢出效果
min-width 0
.item-title
line-height .54rem
font-size .32rem
ellipsis()
.item-desc
line-height .4rem
color #ccc
ellipsis()
.item-button
line-height .44rem
color #fff
margin-top .2rem
padding 0 .2rem
border-radius .6rem
background #ff9300
</style>
4. 编辑’Recommend.vue’ - - - 双向绑定内容
<template>
<div>
<div class="title">热销推荐</div>
<div class="item" v-for="item of recommendList" :key="item.id">
<img class="item-img" :src="item.imgUrl">
<div class="item-info">
<p class="item-title">{{item.title}}</p>
<p class="item-desc">{{item.desc}}</p>
<button class="item-button">查看详情</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'HomeRecommend',
data () {
return {
recommendList: [{
id: '0001',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_vs_tts/9608dc40-1209-4b9d-a73f-1030d1f351fe.gif',
title: '武汉-昆明 9天跟团游',
desc: '9天深度游云南|昆大丽版纳体验升级:省内1动1飞|吉普跟拍+雨林野象谷+雪山'
},
{
id: '0002',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_b2c_001/8a089740-f383-4210-88cf-6d7c61475304.jpg_r_480x320x95_23b8204f.jpg',
title: '武汉-恩施 3天跟团游',
desc: '免票进行时|腾龙洞|夷水丽川|科技激光秀|恩施黄鹤峰林大峡谷|土司城|女儿城'
},
{
id: '0003',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_b2c_001/fda85184-bdbe-45e8-80c7-1c2574922d10.jpg_r_480x320x95_7b5db4d1.jpg',
title: '武汉-武汉欢乐谷 1天跟团游',
desc: '旅行者:惠游湖北纯玩无购物(金龙水寨+汤池温泉)含导游门票'
},
{
id: '0004',
imgUrl: 'https://imgs.qunarzz.com/p/tts9/1711/e5/2033e06bdda7cb02.jpg_r_480x320x95_1c9f89aa.jpg',
title: '武汉-宜昌 2天跟团游',
desc: '冬季滑雪♥真纯玩🔥神农架精华二日游📣情人泉+昭君别院✔赠雪鞋+雪板+雪仗'
}]
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl';
.title
margin-top .2rem
line-height .8rem
background #eee
text-indent .2rem
.item
overflow hidden
display flex
height 1.9rem
background #f5f5f5
.item-img
width 1.7rem
height 1.7rem
margin .1rem
.item-info
flex 1
padding .1rem
// 限定最小宽度,实现文字溢出效果
min-width 0
.item-title
line-height .54rem
font-size .32rem
ellipsis()
.item-desc
line-height .4rem
color #ccc
ellipsis()
.item-button
line-height .44rem
color #fff
margin-top .2rem
padding 0 .2rem
border-radius .6rem
background #ff9300
</style>
七、Weekend
1.创建’Weekend.vue’文件
<template>
<div>weekend</div>
</template>
<script>
export default {
name: 'HomeWeekend'
}
</script>
<style lang="stylus" scoped>
</style>
2. 引用’Weekend.vue’组件 - - - 编辑’Home.vue’文件
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
}
}
</script>
<style>
</style>
3. 编写’Weekend.vue’样式
<template>
<div>
<div class="title">周末去哪儿</div>
<ul>
<li class="item" v-for="item of weekendList" :key="item.id">
<div class="item-img-wrapper">
<img class="item-img" :src="item.imgUrl">
</div>
<div class="item-info">
<p class="item-title">{{item.title}}</p>
<p class="item-desc">{{item.desc}}</p>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HomeWeekend',
data () {
return {
weekendList: [{
id: '0001',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_vs_tts/9608dc40-1209-4b9d-a73f-1030d1f351fe.gif',
title: '武汉-昆明 9天跟团游',
desc: '9天深度游云南|昆大丽版纳体验升级:省内1动1飞|吉普跟拍+雨林野象谷+雪山'
},
{
id: '0002',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_b2c_001/8a089740-f383-4210-88cf-6d7c61475304.jpg_r_480x320x95_23b8204f.jpg',
title: '武汉-恩施 3天跟团游',
desc: '免票进行时|腾龙洞|夷水丽川|科技激光秀|恩施黄鹤峰林大峡谷|土司城|女儿城'
},
{
id: '0003',
imgUrl: 'https://imgs.qunarzz.com/vs_ceph_b2c_001/fda85184-bdbe-45e8-80c7-1c2574922d10.jpg_r_480x320x95_7b5db4d1.jpg',
title: '武汉-武汉欢乐谷 1天跟团游',
desc: '旅行者:惠游湖北纯玩无购物(金龙水寨+汤池温泉)含导游门票'
},
{
id: '0004',
imgUrl: 'https://imgs.qunarzz.com/p/tts9/1711/e5/2033e06bdda7cb02.jpg_r_480x320x95_1c9f89aa.jpg',
title: '武汉-宜昌 2天跟团游',
desc: '冬季滑雪♥真纯玩🔥神农架精华二日游📣情人泉+昭君别院✔赠雪鞋+雪板+雪仗'
}]
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl';
.title
margin-top .2rem
line-height .8rem
background #eee
text-indent .2rem
.item-img-wrapper
overflow hidden
height 0
padding-bottom 37.09%
background #f5
.item-img
width 100%
.item-info
padding .1rem
.item-title
line-height .54rem
font-size .32rem
ellipsis()
.item-desc
line-height .4rem
color #ccc
ellipsis()
</style>
八、Ajax
1.安装axios
npm install axios --save
2.引入Ajax数据 - - - 编辑’Home.vue’
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
methods: {
// 创建一个函数
getHomeInfo () {
// 通过axios进行数据请求
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
console.log(res)
}
},
// 创建一个生命周期函数,当页面挂载好了之后,执行这个函数
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
3.创建本地模拟数据
a. 新建’mock’ 文件夹和 'index.json’文件(路径: src/public/mock/index.json)
{
"ret": true,
"data": {
"city":"北京",
"swiperList": [{
"id": "0001",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg"
},{
"id": "0002",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg"
},{
"id": "0003",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1802/51/e78f936a5b404102.jpg_640x200_c14f0b3a.jpg"
},{
"id": "0004",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1712/91/a275569091681d02.jpg_640x200_0519ccb9.jpg"
}],
"iconList": [{
"id": "0001",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/54/ace00878a52d9702.png",
"desc": "景点门票"
}, {
"id": "0002",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1711/df/86cbcfc533330d02.png",
"desc": "滑雪季"
}, {
"id": "0003",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1710/a6/83f636bd75ae6302.png",
"desc": "泡温泉"
}, {
"id": "0004",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/35/2640cab202c41b02.png",
"desc": "动植园"
}, {
"id": "0005",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/d0/e09575e66f4aa402.png",
"desc": "游乐园"
}, {
"id": "0006",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/59/569d3c096e542502.png",
"desc": "必游榜单"
}, {
"id": "0007",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/17/4bd370f3eb1acd02.png",
"desc": "演出"
}, {
"id": "0008",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/7f/b1ea3c8c7fb6db02.png",
"desc": "城市观光"
}, {
"id": "0009",
"imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/a9/ffc620dbda9b9c02.png",
"desc": "一日游"
}],
"recommendList": [{
"id": "0001",
"imgUrl": "http://img1.qunarzz.com/sight/p0/1409/19/adca619faaab0898245dc4ec482b5722.jpg_140x140_80f63803.jpg",
"title": "故宫",
"desc": "东方宫殿建筑代表,世界宫殿建筑典范"
}, {
"id": "0002",
"imgUrl": "http://img1.qunarzz.com/sight/p0/1511/d2/d2aec2dfc5aa771290.water.jpg_140x140_abb362a7.jpg",
"title": "南山滑雪场",
"desc": "北京专业级滑雪圣地"
}, {
"id": "0003",
"imgUrl": "http://img1.qunarzz.com/sight/p0/1501/f4/f467729126949c3a.water.jpg_140x140_ef235b1c.jpg",
"title": "天安门广场",
"desc": "我爱北京天安门,天安门上太阳升"
}, {
"id": "0004",
"imgUrl": "http://img1.qunarzz.com/sight/p0/1501/40/40b2b6c951b28fdd.water.jpg_140x140_1c863e5c.jpg",
"title": "水立方",
"desc": "中国的荣耀,阳光下的晶莹水滴"
}, {
"id": "0005",
"imgUrl": "http://img1.qunarzz.com/sight/p0/201308/23/b283071686e64dfec8d65eac.jpg_140x140_8c5a7c49.jpg",
"title": "温都水城养生馆",
"desc": "各种亚热带植物掩映其间仿佛置身热带雨林"
}],
"weekendList": [{
"id": "0001",
"imgUrl": "http://img1.qunarzz.com/sight/source/1510/6e/1ea71e2f04e.jpg_r_640x214_aa6f091d.jpg",
"title": "北京温泉排行榜",
"desc": "细数北京温泉,温暖你的冬天"
}, {
"id": "0002",
"imgUrl": "http://img1.qunarzz.com/sight/source/1505/aa/7baaf8a851d221.jpg_r_640x214_1431200f.jpg",
"title": "北京必游TOP10",
"desc": "来北京必去的景点非这些地方莫属"
}, {
"id": "0003",
"imgUrl": "http://img1.qunarzz.com/sight/source/1505/9e/21df651e19af5d.jpg_r_640x214_3ea5bb38.jpg",
"title": "寻找北京的皇城范儿",
"desc": "数百年的宫廷庙宇,至今依旧威严霸气"
}, {
"id": "0004",
"imgUrl": "http://img1.qunarzz.com/sight/source/1505/ce/bc89bc2f0e33ea.jpg_r_640x214_3e408453.jpg",
"title": "学生最爱的博物馆",
"desc": "周末干嘛?北京很多博物馆已经免费开放啦"
}, {
"id": "0005",
"imgUrl": "http://img1.qunarzz.com/sight/source/1505/b2/fde1bfcd057a52.jpg_r_640x214_bbf3fa44.jpg",
"title": "儿童剧场,孩子的乐园",
"desc": "带宝贝观看演出,近距离体验艺术的无穷魅力"
}]
}
}
b. 编辑’.gitignore’文件 - - - 不希望该文件内容提交到线上
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# 不会被提交到线上的仓库里
static/mock
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
c. 编辑’vue.config.js’文件 - - - 实现开发环境的路径替换
const path = require('path')
module.exports = {
devServer: {
proxy: {
'/api': {
// 请求转发到服务器的8080端口上
target: 'http://localhost:8080',
// 替换路径
pathRewrite: {
// '^/api': '/public/mock'
'^/api': '/mock'
}
}
}
},
chainWebpack: (config) => {
config.resolve.alias
.set('styles', path.join(__dirname, './src/assets/styles/'))
.set('@', path.join(__dirname, './src/'))
// .set('common', path.join(__dirname, './src/common/'))
}
}
d. Ajax动态数据请求成功
4.父子组件间传值 - - - 编辑’Home.vue’
<template>
<div>
<home-header :city='city'></home-header>
<home-swiper :List='swiperList'></home-swiper>
<home-icons :List='iconList'></home-icons>
<home-recommend :List='recommendList'></home-recommend>
<home-weekend :List='weekendList'></home-weekend>
</div>
</template>
<script>
// 引用Header组件
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data () {
return {
city: '',
swiperList: [],
iconList: [],
recommendList: [],
weekendList: []
}
},
methods: {
// 创建一个函数
getHomeInfo () {
// 通过axios进行数据请求
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
// console.log(res)
res = res.data
// 如果后端返回了数值,并且有data这个数据项
if (res.ret && res.data) {
// 定义一个'data'='res.data'
const data = res.data
// ajix里面的'data.city'传给了'city'
this.city = data.city
// swiperList里面的数据进行传输
this.swiperList = data.swiperList
// iconList里面的数据进行传输
this.iconList = data.iconList
// recommendList里面的数据进行传输
this.recommendList = data.recommendList
// weekendList里面的数据进行传输
this.weekendList = data.weekendList
}
}
},
// 创建一个生命周期函数,当页面挂载好了之后,执行这个函数
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
5.Header接收数据 - - - 编辑’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">
<!-- 城市 -->
{{this.city}}
<span class="iconfont arrow-icon"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader',
props: {
city: String
}
}
</script>
<style lang='stylus' scoped>
// 引用'varibles.styl'变量样式
@import '~styles/varibles.styl'
.header
display flex
// width 100%
height $headerHight
background-color $bgColor
color #fff
text-align center
line-height $headerHight
.header-left
float left
width .64rem
.back-icon
font-size .6rem
.header-input
flex 1
margin-left .2rem
margin-top .12rem
padding-left .2rem
height .64rem
text-align left
line-height .64rem
color #ccc
border-radius .1rem
background #fff
.header-right
float right
width 1.24rem
padding 0 .1rem
.arrow-icon
margin-left -.1rem
font-size .4rem
</style>
6.Swiper接收数据 - - - 编辑’Swiper.vue’
<template>
<div class="wrapper">
<!-- 'v-if'是为了初次创建由完整的数组来创建,而不是空数组来创建(轮播图第一页不是默认的) -->
<!-- <swiper :options="swiperOption" v-if="list.length"> 这种写法不够优雅,模板中不要涉及计算公式 -->
<swiper :options="swiperOption" v-if="showSwiper">
<swiper-slide v-for="item of List" :key="item.id">
<img class='swiper-img' :src="item.imgUrl" alt="">
</swiper-slide>
<!-- 分页 -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
props: {
List: Array
},
data () {
return {
swiperOption: {
// 填入一个配置项
pagination: '.swiper-pagination',
// 循环轮播
loop: true
}
}
},
// 创建了个计算公式,可以被引用
computed: {
showSwiper () {
return this.List.length
}
}
}
</script>
<style lang="stylus" scoped>
// 通过鼠标右键审查元素定位要'class',从而修改样式(stylus的样式穿透)
.wrapper >>> .swiper-pagination-bullet-active
background #fff
// background #fff !important
.wrapper
height 0
padding-bottom 31.25%
overflow hidden
background #fff
.swiper-img
width 100%
</style>
7.Icons接收数据 - - - 编辑’Icons.vue’
<template>
<div class="icons">
<swiper :options="swiperOption">
<swiper-slide v-for="(page, index) of pages" :key="index">
<div class="icon" v-for="item of page" :key="item.id">
<div class="icon-img">
<img class="icon-img-content" :src="item.imgUrl" alt="">
</div>
<p class="icon-desc">{{item.desc}}</p>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeIcons',
props: {
List: Array
},
data () {
return {
swiperOption: {
// 防止轮播自动播放
autoplay: false
}
}
},
// 计算
computed: {
// 定义一个函数
pages () {
// 申明一个空数组,作为传递所有item后总共的页数
const pages = []
// 将iconList循环一遍,可以获取到两个数据'循环项'和它对应的'下标'
this.List.forEach((item, index) => {
// 定义一个'page',计算页数 page = index/8
const page = Math.floor(index / 8)
// page = index / 8 < 1时,pages[0]不变,不执行条件
// page = index / 8 >= 1时,pages[page]和pages[0]不一致,执行条件
if(!pages[page]) {
// 重新创建一个空数组,然后都把新数据添加至空数组当前页
pages[page] = []
}
pages[page].push(item)
})
return pages
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
// 右键审查元素定位到'class',从而修改样式
.icons >>> .swiper-container
// swiper自带overflow属性
// overflow hidden
height 0
padding-bottom 50%
// background #f5f5f5
.icons
margin-top .1rem
.icon
position relative
float left
width 25%
height 0
padding-bottom 25%
// background skyblue
overflow hidden
.icon-img
position absolute
left 0
top 0
right 0
bottom .44rem
padding .1rem
box-sizing border-box
.icon-img-content
display block
height 100%
margin 0 auto
.icon-desc
position absolute
left 0
right 0
bottom 0
height .44rem
line-height .44rem
text-align center
color $darkTextColor
// 封装的文字溢出效果
ellipsis()
</style>
8.Recommend接收数据 - - - 编辑’Recommend.vue’
<template>
<div>
<div class="title">热销推荐</div>
<ul>
<li class="item" v-for="item of List" :key="item.id">
<img class="item-img" :src="item.imgUrl">
<div class="item-info">
<p class="item-title">{{item.title}}</p>
<p class="item-desc">{{item.desc}}</p>
<button class="item-button">查看详情</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HomeRecommend',
props: {
List: Array
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl';
.title
margin-top .2rem
line-height .8rem
background #eee
text-indent .2rem
.item
overflow hidden
display flex
height 1.9rem
background #f5f5f5
.item-img
width 1.7rem
height 1.7rem
margin .1rem
.item-info
flex 1
padding .1rem
// 限定最小宽度,实现文字溢出效果
min-width 0
.item-title
line-height .54rem
font-size .32rem
ellipsis()
.item-desc
line-height .4rem
color #ccc
ellipsis()
.item-button
line-height .44rem
color #fff
margin-top .2rem
padding 0 .2rem
border-radius .6rem
background #ff9300
</style>
9.Recommend接收数据 - - - 编辑’Recommend.vue’
<template>
<div>
<div class="title">热销推荐</div>
<ul>
<li class="item" v-for="item of List" :key="item.id">
<img class="item-img" :src="item.imgUrl">
<div class="item-info">
<p class="item-title">{{item.title}}</p>
<p class="item-desc">{{item.desc}}</p>
<button class="item-button">查看详情</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HomeRecommend',
props: {
List: Array
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl';
.title
margin-top .2rem
line-height .8rem
background #eee
text-indent .2rem
.item
overflow hidden
display flex
height 1.9rem
background #f5f5f5
.item-img
width 1.7rem
height 1.7rem
margin .1rem
.item-info
flex 1
padding .1rem
// 限定最小宽度,实现文字溢出效果
min-width 0
.item-title
line-height .54rem
font-size .32rem
ellipsis()
.item-desc
line-height .4rem
color #ccc
ellipsis()
.item-button
line-height .44rem
color #fff
margin-top .2rem
padding 0 .2rem
border-radius .6rem
background #ff9300
</style>
更多推荐
已为社区贡献7条内容
所有评论(0)