通过前面的学习已经初步掌握了Vue的相关基础知识,这周主要是通过一个小项目对Vue的相关知识进行学习。
项目详细代码链接,实时更新(https://github.com/TaiYiZhen/supermall.git)
一.自定义TabBar组件
首先新建一个项目tabbar用来封装自定义TabBar组件以便于在后期项目中使用
实现思路:
1. 如果在下方有一个单独的TabBar组件,你如何封装 自定义TabBar组件,在APP中使用 让TabBar出于底部,并且设置相关的样式
2.TabBar中显示的内容由外界决定 定义插槽 flex布局平分TabBar
3.自定义TabBarItem,可以传入 图片和文字 定义TabBarItem,并且定义两个插槽:图片、文字。 给两个插槽外层包装div,用于设置样式。 填充插槽,实现底部TabBar的效果
4.传入 高亮图片 定义另外一个插槽,插入active-icon的数据 定义一个变量isActive,通过v-show来决定是否显示对应的icon
5.TabBarItem绑定路由数据 安装路由:npm install vue-router —save 完成router/index.js的内容,以及创建对应的组件 main.js中注册router APP中加入<router-view>组件
6.点击item跳转到对应路由,并且动态决定isActive 监听item的点击,通过this.$router.replace()替换路由路径 通过this.$route.path.indexOf(this.link) !== -1来判断是否是active
7.动态计算active样式 封装新的计算属性:this.isActive ? {'color': 'red'} : {}
主要代码如下
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar"
}
</script>
<style scoped>
/*使用flex布局让其均等分,居中*/
#tab-bar {
display: flex;
background-color: #f6f6f6;
/*置底显示*/
position: fixed;;
left: 0;
right: 0;
bottom: 0;
/*阴影*/
box-shadow: 0 -3px 1px rgba(100, 100, 100, .1);
}
</style>
<template>
<div class="tab-bar-item" @click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<div :style="activeStyle"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default {
name: "TabBarItem",
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
data(){
return{
/*isActive:true*/
}
},
computed:{
isActive(){
return this.$route.path.indexOf(this.path)!==-1
},
activeStyle(){
return this.isActive ? {color: this.activeColor} : {}
}
},
/*监听事件*/
methods:{
itemClick(){
this.$router.replace(this.path);
}
}
}
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: middle;
}
</style>
二.axios网络请求模块封装(+拦截器)
封装一个axios网络请求模块,便于后期开发维护
import axios from 'axios'
export function request(config) {
/*创建axios实例*/
const instance = axios.create({
baseURL: '接口地址',
timeout: 5000
})
/*axios的拦截器*/
/*拦截请求*/
instance.interceptors.request.use(config => {
console.log(config);
/*为什么要拦截请求:*/
/*比如config中有一些信息不符合服务器要求*/
/*比如每次发送网络请求时希望显示请求图标*/
/*某些网络请求(比如登陆)必须携带一些特殊信息*/
/*f返回请求*/
return config;
}, err => {
console.log(err);
}
);
/*拦截响应*/
instance.interceptors.response.use(res=>{
console.log(res);
/*返回响应*/
return res.data;
}, err => {
console.log(err);
}
);
/*发送真正的网络请求*/
return instance(config)
}
/*export function request(config) {
return new Promise((resolve, reject) => {
/!*创建axios实例*!/
const instance=axios.create({
baseURL:'接口地址',
timeout:5000
})
/!*发送真正的网络请求*!/
instance(config)
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}*/
/*export function request(config) {
/!*创建axios实例*!/
const instance=axios.create({
baseURL:'接口地址',
timeout:5000
})
/!*发送真正的网络请求*!/
instance(config.baseConfig)
.then(res=>{
/!*console.log(res);*!/
config.success(res)
})
.catch(err =>{
/!*console.log(err);*!/
config.failure(err)
})
}*/
/*
export function request(config,success,failure) {
/!*创建axios实例*!/
const instance=axios.create({
baseURL:'接口地址',
timeout:5000
})
/!*发送真正的网络请求*!/
instance(config)
.then(res=>{
/!*console.log(res);*!/
success(res)
})
.catch(err =>{
/!*console.log(err);*!/
failure(err)
})
}*/
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
/*axios简单使用*/
/*axios({
url: '接口地址',
methods: 'get'
}).then(res => {
console.log(res);
})
axios({
url: '接口地址',
//专门针对get请求的参数拼接
params: {
type: 'pop',
page: 1
}
}).then(res => {
console.log(res);
})*/
/*axios发送并发请求*/
/*axios.all([axios({
url: '接口地址'
}), axios({
url: '接口地址',
params: {
type: 'pop',
page: 1
}
})]).then(results => {
console.log(results);
console.log(results[0]);
console.log(results[1]);
})*/
/*使用全局的axios和对应的配置在进行网络请求*/
/*axios.defaults.baseURL='接口地址'
axios.defaults.timeout=5000
axios.all([axios({
url: '/home/multidata'
}), axios({
url: '/home/data',
params: {
type: 'pop',
page: 1
}
})]).then(axios.spread((res1,res2)=> {
console.log(res1);
console.log(res2);
}))*/
/*/!*创建对应的axios的实例*!/
const instance1=axios.create({
baseURL:'接口地址',
timeout:5000
})
instance1({
url:'/home/multidata'
}).then(res=>{
console.log(res);
})
instance1({
url:'/home/data',
params: {
type: 'pop',
page: 1
}
}).then(res=>{
console.log(res);
})*/
/*封装request模块*/
import {request} from "./network/request";
request({
url:'/home/multidata'
}).then(res=>{
console.log(res);
},err=>{
console.log(err);
})
/*request({
baseConfig:{
},
success:function (res) {
},
failure:function (err) {
}
})*/
/*request({
url:'/home/multidata'
},res=>{
console.log(res);
},err=>{
console.log(err);
})*/
三.首页开发
首页开发初步效果如下(代码太多不做展示,对源码感兴趣的见文章顶部链接)
项目采用组件化开发思想
首页主要由以下组件构成:
NavBar组件:构成顶部导航栏
轮播组件:构成首页顶端的轮播效果
RecommendView:首页推荐信息的展示
FeatureView:首页本周流行展示(该组件比较简单)由图片加链接构成
tabControl:首页商品类型切换(流行,新款,精选)
GoodsList:商品数据展示
Scroll:原生的滚动效果不理想,采用betterscroll组件
BackTop:回到顶部
四.详情页开发
解决了轮播不畅的问题
<template>
<div>
<swiper class="detail-swiper" v-if="topImages.length">
<swiper-item class="swiper-item" v-for="(item, index) in topImages" :key="index">
<img :src="item" alt="">
</swiper-item>
</swiper>
</div>
</template>
添加v-if="topImages.length"解决
详情页开发效果如下: