项目部分
Vue项目: 基础 webapp
首页模块 城市列表页 列表页-》详情页 公共图片 css部分 stylus(scss less)
环境准备1:
1:移动端项目开发环境准备:
安装nodejs
验证 node -v npm -v
注册码云 https://gitee.com/ 代码版本管理 国内 快速 简单 代码托管平台(网站) github.com(全球最大的免费代码托管平台(网站)) gitlab
注册账号
创建仓库
git 分布式代码版本控制管理工具
安装git
使用git
本地搭建 vue-cli2 或者vue-cli3 vue-cli4 至少工作2年 vue-cli2 vue3 今年出来的 vite
全局安装
验证vuecli安装是否完成
vue init webpack 项目名
npm run dev (npm run serve)
本地仓库代码与远程仓库一致
git add .
git commit -m “travel init”
git push
环境准备2:
项目代码结构: 面试当中会问到 课后去记忆并表达出来 检查(每天都有表达练习课 答不出来 红包)
单文件组件及vue中的路由
main.js 解释
App.vue 根组件
单文件组三部分组成
App.vue中 router-view显示的是当前路由地址所对应的内容 一级路由 嵌套路由
路由配置文件都放在 router目录下的 index.js文件下
单页应用与多页应用的区别: 面试题
多页应用 MPA
页面跳转 返回HTML文件 a标签跳转
优点:首屏时间快 SEO效果好(一个http请求) SEO可以识别网页
缺点:页面切换慢
单页应用 SPA
页面跳转 JS渲染 uri地址
优点:页面切换快
缺点:首屏时间稍慢 SEO差 -->改进 ssr 服务端渲染技术 vue-》nuxt.js
项目代码的初始化
index.html中添加视口 发大 缩小无效
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
rem.js 设备通过js自动适配
统一样式 保证页面在所有浏览器显示效果一致。 reset.css
放在目录 assets/styles下
1px边框问题 手机像素比较高 是一个2倍屏 3倍屏 虽然设置为 border 1px solid 但实际上有2px 3px;
解决方案 border.css css3的缩放来解决 谷歌浏览器 强制12px 怎么做 谷歌浏览器能出来10px 8px css3的缩放来解决
300毫秒点击延迟问题:
click事件延迟300ms执行
cnpm install fastclick --save
全局引入 main.js
import fastClick from 'fastclick'
fastClick.attach(document.body)
字体图标下载
iconfont
www.iconfont.cn 注册账号 创建项目 选择图标 下载
iconfont.woff2
iconfont.woff
iconfont.ttf
iconfont.svg
iconfont.eot
把它们放到styles/iconfont目录下
项目开始
采用stylus css预处理器 scss less stylus(公司用) 没用过就得用
cnpm install stylus --save-dev
cnpm install stylus-loader@3.0.2 --save-dev
Home页布局
HomeHeader组件
www.iconfont.cn中下载字体图标及使用
两种引入方法一样 @ 代表的是src 哪在哪定义的呢
第一种
/* @import "../../../assets/styles/varibles.styl"; */
第二种
@import "~@/assets/styles/varibles.styl";
更改项目文件中的相对路径
在build/webpack.base.conf.js中修改
在’@': resolve(‘src’),
后添加一条
‘styles’: resolve(‘src/assets/styles’),
配置完后 重启
首页我们先创建 首页 详情页 城市页路由和页面
配置路由在router/index.js中配置
export default new Router({
mode:"history",
routes: [
{
path: '/',
name: 'Home',
component: () => import('../views/home/home.vue')
},
{
path: '/city',
name: 'City',
component: () => import('../views/city/City.vue')
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('../views/detail/Detail.vue')
}
]
})
然后在src 目录下创建一个views文件夹,用来放置页面的,我们可以在weiws里边创建不同的页面,比如有home,city,detail等页面创建页面可以仿照路由的懒加载路由的层级进行创建页面
首页布局
首先,我们先在view文件夹里边创建一个home文件夹,在home文件夹里边创建一个home.vue 当做首页,在home.vue平级创建一components文件夹,用来放置子组件
首页主页面
布局
<div class="home">
<home-header @gocity="gocity"></home-header>
<home-swiper></home-swiper>
<home-desc></home-desc>
<home-esell @godetail="godetail"></home-esell>
<home-recommend></home-recommend>
</div>
js
引入所有子组件
import homeHeader from "./components/homeHeader";
import homeSwiper from "./components/homeSwiper";
import homeDesc from "./components/homeDesc";
import homeEsell from "./components/homeEsell";
import homeRecommend from "./components/homeRecommend";
首页跳转城市页和详情页,功能如下,跳转详情可以通过冬天路由传参
methods: {
godetail(id) {
// console.log(id);
this.$router.push({
name: "Detail",
params: {
id: id
}
});
},
gocity(){
this.$router.push('/city')
}
},
components: {
homeHeader,
homeSwiper,
homeDesc,
homeEsell,
homeRecommend
}
scc
.home {
background-color: #eee;
height: 100%;
p {
margin: 0.265rem;
}
}
头部布局
我可以在components里边创建一个homeHeader.vue的文件
布局
<div class="homeHeader">
<div class="left">
<div class="iconfont icon_left"></div>
</div>
<div class="center">
<span class=" iconfont" ></span>
<input type="text" placeholder="请输入城市/景点/游玩主题"></input>
</div>
<div class="right">
<div class="iconfont" @click="gocity">{{ this.$store.state.city }}</div>
</div>
</div>
scc
@import '~styles/varibles.styl';
.homeHeader
display: flex;
background-color: $bgColor;
height: 0.86rem;
// align-items: center;
line-height: 0.86rem;
color: #ffffff;
.left
width: 0.64rem;
float: left;
.icon_left
text-align: center;
font-size: 0.4rem;
.center
flex: 1;
display: flex;
position: relative;
span
position: absolute;
left: 0.2rem;
color: #ccc;
input
flex: 1;
// width: 100%;
height: 0.64rrem;
line-height: 0.64rem;
margin-top: 0.12rem;
margin-bottom: 0.12rem;
margin-left: 3.2px;
padding-left: 0.5rem;
border-radius: 0.1rem;
.right
min-width: 1.04rem;
padding: 0 0.1rem;
float: right;
功能 传到父组件然后进行跳转城市页面
methods: {
gocity(){
this.$emit("gocity")
}
},
轮播图实现模块
在home.vue平级创建一components文件夹里边创建一个homeSwiper.vue文件,用来放置轮播图组件
在写轮播图的时候我们需要下载vue-awesome-swiper
插件
下载 cnpm install vue-awesome-swiper@2.6.7 --save
全局引入
import VueAwesomeSwiper from 'vue-awesome-swiper'
import "swiper/dist/css/swiper.css"
Vue.use(VueAwesomeSwiper)
布局
<div>
<swiper >
<!-- slides -->
<swiper-slide v-for="(item, index) in swiperList" :key="index">
<img class="swiper-img" :src="item.imgUrl" >
</swiper-slide>
<!-- Optional controls -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
<div>test</div>
</div>
js
data() {
return {
swiperList: []
};
},
created() {
this.$axios.get("../static/mock/index.json").then(res => {
// console.log(res);
this.swiperList = res.data.data.swiperList;
});
},
css
div {
overflow: hidden;
width: 100%;
height: 0;
padding-bottom: 26.67%;
}
热门景点模块
在home.vue平级创建一components文件夹里边创建一个homeDesc.vue文件,用来放置热门景点组件
布局
<div class="icons">
<swiper :options="swiperOption">
<swiper-slide v-for="(page, index) in pages" :key="index">
<div class="icon" v-for="item in page" :key="item.id">
<div class="icon-img">
<img class="icon-img-content" :src="item.imgUrl" >
</div>
<p class="icon-desc">{{item.desc}}</p>
</div>
</swiper-slide>
</swiper>
</div>
js
data() {
return {
iconList: [],
swiperOption: {
autoplay: false
}
};
},
computed: {
pages() {
const pages = [];
this.iconList.forEach((item, index) => {
const page = Math.floor(index / 8);
if (!pages[page]) {
pages[page] = [];
}
pages[page].push(item);
});
return pages;
}
},
created() {
this.$axios.get("../static/mock/index.json").then(res => {
// console.log(res);
this.iconList = res.data.data.iconList;
});
}
css
.icons >>> .swiper-container {
height: 0;
padding-bottom: 50%;
background-color: #fff;
}
.icon {
position: relative;
overflow: hidden;
float: left;
width: 25%;
height: 0;
padding-bottom: 25%;
// background : red
.icon-img {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0.44rem;
// background :blue
box-sizing: border-box;
padding: 0.05rem;
.icon-img-content {
display: block;
margin: 0 auto;
height: 100%;
}
}
.icon-desc {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 0.44rem;
line-height: 0.44rem;
text-align: center;
color: #333;
}
}
热门推荐模块
在home.vue平级创建一components文件夹里边创建一个homeEsell.vue文件,用来放置热门推荐组件
布局
<div class="homeEsell">
<div class="title">
热销推荐
</div>
<ul>
<li class="item border-bottom" v-for="(item, index) in recommendList" :key="index">
<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" @click="godetail(item.id)">查看详情</button>
</div>
</li>
</ul>
</div>
js
data() {
return {
recommendList: []
};
},
created() {
this.$axios.get("../static/mock/index.json").then(res => {
// console.log(res);
this.recommendList = res.data.data.recommendList;
// console.log(this.recommendList);
});
},
methods: {
godetail(id){
// console.log(id);
// this.$router.push('/detail')
// this.$router.push({
// path: '/detail',
// query: {
// id:id
// },
// })
this.$emit('godetail',id)
}
},
scc
@import '~styles/minxins.styl';
.title {
margin-top: 0.2rem;
line-height: 0.8rem;
background: #eee;
text-indent: 0.2rem;
}
.item {
overflow: hidden;
display: flex;
height: 1.9rem;
// background :red
background-color: #fff;
.item-img {
width: 1.7rem;
height: 1.7rem;
padding: 0.1rem;
}
.item-info {
flex: 1;
padding: 0.1rem;
min-width: 0;
.item-title {
line-height: 0.54rem;
font-size: 0.32rem;
ellipsis();
}
.item-desc {
line-height: 0.4rem;
color: #ccc;
ellipsis();
}
.item-button {
line-height: 0.44rem;
margin-top: 0.16rem;
background: #ff9300;
padding: 0 0.2rem;
border-radius: 0.06rem;
color: #fff;
}
}
}
周末去哪儿模块
在home.vue平级创建一components文件夹里边创建一个homeRecommend.vue文件,用来放置周末去哪儿组件
代码
<div>
<div class="title">
周末去哪儿
</div>
<ul>
<router-link to="/detail">
<li class="item border-bottom" v-for="item of recommendList" :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>
</router-link>
</ul>
</div>
js
data() {
return {
recommendList: []
};
},
created() {
this.$axios.get("../static/mock/index.json").then(res => {
// console.log(res);
this.recommendList = res.data.data.recommendList;
// console.log(this.recommendList);
});
}
css
@import '~styles/minxins.styl';
.title {
line-height: 0.8rem;
background: #eee;
text-indent: 0.2rem;
}
.item-img-wrapper {
overflow: hidden;
height: 0;
padding-bottom: 37.09%;
background: #999;
.item-img {
width: 100%;
}
}
.item-info {
padding: 0.1rem;
.item-title {
line-height: 0.54rem;
font-size: 0.32rem;
ellipsis();
}
.item-desc {
line-height: 0.4rem;
color: #ccc;
ellipsis();
}
}
详情页布局
首先,我们先在view文件夹里边创建一个detail文件夹,在detail文件夹里边创建一个detail.vue 当做首页,在detail.vue平级创建一components文件夹,用来放置子组件
详情页主页面
在detail里边创建一个detail.vue当做主页面
布局
<div class="box">
<detailHeader v-show="flag"></detailHeader>
<Banner :detailList="detailList" @gobox="gobox"></Banner>
<gallary v-show="show" @close="close" :detailList="detailList"></gallary>
<div class="content"></div>
<div>
<router-link tag="div" to="/" class="header-abs" v-show="showAbs">
<div class="iconfont header-abs-back"></div>
</router-link>
<div class="header-fixed" v-show="!showAbs" :style="opacityStyle" >
<router-link to="/">
<div class="iconfont header-fixed-back"></div>
</router-link>
景点详情
</div>
</div>
</div>
js
引入
import Banner from "./components/Banner.vue";
import Gallary from "./components/Gallary.vue";
import detailHeader from "./components/detailHeader.vue";
功能
data() {
return {
detailList: {},
show: false,
flag: false,
showAbs: true,
opacityStyle: {
opacity: 0
}
};
},
created() {
// console.log(this.$route.params.id);
this.$axios.get("../static/mock/detail.json").then(res => {
console.log(res);
this.detailList = res.data.data;
});
},
methods: {
gobox() {
this.show = true;
},
close() {
this.show = false;
},
handleScroll() {
//document.documentElement.scrollTop pc端使用 document.body.scrollTop 移动端使用
console.log(document.body.scrollTop);
const top = document.body.scrollTop;
if (top > 60) {
let opacity = top / 140;
opacity = opacity > 1 ? 1 : opacity;
this.opacityStyle = { opacity };
this.showAbs = false;
} else {
this.showAbs = true;
}
}
},
components: {
Banner,
Gallary,
detailHeader
}
scc
.box {
height: 100%;
}
.content {
height: 50rem;
}
@import '~styles/varibles.styl';
.header-abs {
position: absolute;
left: 0.2rem;
top: 0.2rem;
width: 0.8rem;
height: 0.8rem;
line-height: 0.8rem;
border-radius: 0.4rem;
text-align: center;
background: rgba(0, 0, 0, 0.8);
.header-abs-back {
color: #fff;
font-size: 0.4rem;
}
}
.header-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
height: $headerHeight;
line-height: $headerHeight;
text-align: center;
color: #fff;
background: $bgColor;
font-size: 0.32rem;
.header-fixed-back {
position: absolute;
top: 0;
left: 0;
width: 0.64rem;
text-align: center;
font-size: 0.4rem;
color: #fff;
}
}
详情页的轮播图布局
布局
<div class="container">
<div class="wrapper">
<swiper :options="swiperOption" class="swiper-ctn">
<swiper-slide v-for="(item, index) in detailList.gallaryImgs" :key="index">
<img class="gallary-img" :src="item" @click="close">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</div>
js
props: {
detailList: Object
},
data() {
return {
swiperOption: {
autoplay: false
},
pagination: ".swiper-pagination",
paginationType: "fraction",
observeParents: true,
observer: true
};
},
methods: {
close() {
this.$emit("close");
}
}
css
.container >>> .swiper-container {
overflow: inherit;
}
.container {
display: flex;
flex-direction: column;
// align-items center;
justify-content: center;
z-index: 99;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: #000;
.wrapper {
height: 0;
width: 100%;
padding-bottom: 100%;
.gallary-img {
width: 100%;
}
.swiper-pagination {
color: #fff;
bottom: -1rem;
}
}
}
详情页文字描述布局
代码
<div>
<div class="banner">
<img class="banner-img" :src="detailList.bannerImg" @click="gobox">
<div class="banner-info">
<div class="banner-title">
{{detailList.sightName}}
</div>
<div class="banner-number">
<span class="iconfont banner-icon" ></span>
<!-- {{detailList.gallaryImgs.length}} -->
2
</div>
</div>
</div>
<ul class="ul">
<li class="li" v-for="(item, index) in detailList.categoryList" :key="index">
<div class="incofont">{{item.title}}
<div class="tlt" v-for="(n, i) in item.children" :key="i">
<div>{{n.title}}
<div class="ctn" v-for="(a, b) in n.children" :key="b">
{{a.title}}
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
js
props: {
detailList: Object
},
data() {
return {};
},
methods: {
gobox(){
// console.log('sdaf');
this.$emit("gobox")
}
},
css
.banner {
position: relative;
overflow: hidden;
height: 0;
padding-bottom: 55%;
.banner-img {
width: 100%;
}
.banner-info {
display: flex;
position: absolute;
left: 0;
right: 0;
bottom: 0;
line-height: 0.6rem;
color: #fff;
background-image: linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8));
.banner-tittle {
flex: 1;
font-size: 0.32rem;
padding: 0 0.2rem;
}
.banner-number {
height: 0.32rem;
line-height: 0.32rem;
margin-top: 0.14rem;
margin-left: auto;
padding: 0 0.4rem;
border-radius: 0.2rem;
background: rgba(0, 0, 0, 0.8);
font-size: 0.24rem;
.banner-icon {
font-size: 0.24rem;
}
}
}
}
.ul {
li {
margin-left: 0.625rem;
margin-top: 0.2rem;
display: block;
div{
margin .2rem
}
}
}
城市布局
首先,我们先在view文件夹里边创建一个city文件夹,在city文件夹里边创建一个city.vue 当做首页,在city.vue平级创建一components文件夹,用来放置子组件
城市主页布局
布局
<div class="city">
<city-header></city-header>
<citySearch :cities = "cities" ></citySearch>
<cityList :cities = "cities" :hot= "hotCities" :letter = "letter"></cityList>
<!-- <city-alphabet :cities = "cities"></city-alphabet> -->
<city-alphabet :cities="cities" @change = "handleLetterChange"></city-alphabet>
</div>
js
引入
import cityHeader from "./components/cityHeader";
import citySearch from "./components/citySearch";
import cityList from "./components/cityList";
import cityAlphabet from "./components/cityAlphabet";
功能
data() {
return {
hotCities: [],
cities: {},
letter: ""
};
},
created() {},
methods: {
getCityInfo() {
this.$axios.get("static/mock/city.json").then(this.handleGetCityInfoSucc);
},
handleGetCityInfoSucc(res) {
res = res.data;
// console.log(res.data);
if (res.ret && res.data) {
const data = res.data;
this.cities = data.cities;
this.hotCities = data.hotCities;
}
},
handleLetterChange(letter) {
// console.log(letter);
this.letter = letter;
}
},
mounted() {
this.getCityInfo();
},
components: {
cityHeader,
citySearch,
cityList,
cityAlphabet
}
城市子组件搜索组件
布局
<div class="search">
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="输入城市名或拼音" />
</div>
<div class="search-content" ref="search" v-show="keyword">
<ul>
<li class="search-item border-bottom" v-for="item of list" :key="item.id" @click="handleCityClick(item.name)" >
{{ item.name }}
</li>
<li class="search-item border-bottom" v-show="hasNoData">
没有找到匹配数据
</li>
</ul>
</div>
</div>
js
import Bscroll from "better-scroll";
props: {
cities: Object
},
data() {
return {
keyword: "",
list: []
};
},
methods: {
handleCityClick(city) {
this.$store.commit("changeCityValue", city);
this.$router.push("/")
}
},
watch: {
keyword() {
//函数节流
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
const result = [];
for (let i in this.cities) {
this.cities[i].forEach(value => {
//如果我们能从name spell当中搜索到 我们就把它添加到result当中
if (
value.spell.indexOf(this.keyword) > -1 ||
value.name.indexOf(this.keyword) > -1
) {
result.push(value);
}
});
}
this.list = result;
}, 100);
}
},
mounted() {
//搜索出来内容的滚动
this.scroll = new Bscroll(this.$refs.search);
},
computed: {
hasNoData() {
return !this.list.length;
}
}
css
@import '~styles/varibles.styl';
.search {
height: 0.72rem;
padding: 0 0.1rem;
background: $bgColor;
.search-input {
box-sizing: border-box;
width: 100%;
height: 0.62rem;
padding: 0 0.1rem;
line-height: 0.62rem;
text-align: center;
border-radius: 0.06rem;
color: #666;
}
}
.search-content {
z-index: 1;
overflow: hidden;
position: absolute;
top: 1.58rem;
left: 0;
right: 0;
bottom: 0;
background: #fff;
.search-item {
line-height: 0.82rem;
padding-left: 0.5rem;
background: #fff;
color: #666;
}
}
当前城市子组件
布局
<div class="list" ref="wrapper">
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">{{ this.$store.state.city }}</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">热门城市</div>
<div class="button-list">
<div class="button-wrapper" v-for="item in hot" :key="item.id" @click="handleCityClick(item.name)">
<div class="button">{{ item.name }}</div>
</div>
</div>
</div>
<div class="area" >
<div class="title border-topbottom content" v-for="(item, index) in cities" :key="index" :ref="index">
{{index}}
<div class="item-list" v-for="i in item" :key="i.id" @click="handleCityClick(i.name)">
{{i.name}}
</div>
</div>
</div>
</div>
js
在这里我们需要用到better-scroll插件
可以在www.github.com 上搜找Better-scroll 是基于iscroll的封装 原理
安装命令 cnpm install better-scroll@1.8.1 --save
import Bscroll from "better-scroll";
props: {
cities: Object,
hot: Array,
letter: String
},
data() {
return {};
},
created() {},
methods: {
handleCityClick(city) {
this.$store.commit("changeCityValue", city);
this.$router.push("/");
}
},
mounted() {
this.scroll = new Bscroll(this.$refs.wrapper);
},
watch: {
letter() {
// console.log(this.letter);
if (this.letter) {
const element = this.$refs[this.letter][0];
console.log(element);
this.scroll.scrollToElement(element);
}
}
}
css
@import '~styles/varibles.styl';
.border-topbottom {
&:before {
border-color: #ccc;
}
&:after {
border-color: #ccc;
}
}
.border-bottom {
&:before {
border-color: #ccc;
}
}
.list {
position: absolute;
top: 1.58rem;
left: 0;
right: 0;
bottom: 0;
.title {
line-height: 0.54rem;
background: #eee;
// padding-left: 0.2rem;
color: #666;
font-size: 0.26rem;
}
.button-list {
overflow: hidden;
padding: 0.1rem 0.6rem 0.1rem 0.1rem;
.button-wrapper {
float: left;
width: 33.33%;
.button {
margin: 0.1rem;
padding: 0.1rem 0;
text-align: center;
border: 0.02rem solid #ccc;
border-radius: 0.06rem;
}
}
}
.item-list {
line-height: 0.76rem;
padding-left: 0.2rem;
border-bottom: 1px solid #eee;
background-color: #fff;
}
.content {
background-color: #eee;
}
}
以上是这个项目的所有代码和总结