vue进阶之——仿去哪App
去哪App源码:
https://gitee.com/syinling/syl-travel.git
一.技术栈:
Vue-cli+axios+better-scroll+sass-loader+swiper+mockjs+iconfont
二.关于移动端适配问题的css样式
import '../assets/style/reset.css' 各种浏览器适配样式
import '../assets/style/border.css' 1像素边框问题
三.vue-cli项目框架搭建
cmd进入系统命令命令:
全局安装这个工具
1.npm install -g @vue/cli
2.vue create travel
个人建议:自己可以先创建自己的模板,项目可以套用这个,就不需要反复下载安装包了
安装:vue-router/vuex/vue-axios/各种需要的插件
在项目src文件夹下,创建store/router目录
````
import Vuefrom 'vue'
import Vuex from 'vuex'
import cityListfrom './modules/citylist.js'
Vue.use(Vuex);
let store =new Vuex.Store({
modules:{
cityList //store也可以有路由
},
});
export default store;
router也一样 main.js中记得引用
+page文件夹 存放各个页面
+mock文件夹 mock数据
+utils文件夹 存放第三方例如axios代码
+plugin文件夹 可以自定义混入插件 代码实例:
export default {
install(Vue) {
Vue.mixin({
filters:{
// filterABC(value) {
// return 'ABCDEFGHJKLMNPQRSTWXYZ'[(value%15)];
//}
},
methods:{
},
data(){
return {
}
}
});
}
}
````
mian.js中引用 这个插件:
````
import SylPluginfrom '@/plugin'
Vue.use(SylPlugin,{});
+config文件夹 可以说明全局的变量
例:
const BASE_URL_DEV='';
const BASE_URL_TEST='http://xxxx.xxx.xx.xx:8081';
const BASE_URL_PRO='http://xxxx.xxx.xx.xx:8082';
//减少使用魔法数字,使用解构类型,让代码更加语义化
const MODE_TYPE={
DEV:0,
TEST:1,
PRO:2
};
const MODE=MODE_TYPE.DEV;
export const BASE_URL=[BASE_URL_DEV,BASE_URL_TEST,BASE_URL_PRO][MODE];
export const SUBJECT_TYPE={
DX:0,
DUOX:1,
PD:2,
JD:3,
};
····
在根目录下可以加上vue.config.js 设置代理等
项目搭建完毕
四.项目vue新增知识点
4.1. `home页面----header.vue 中 城市名称的缓存 页面刷新,城市不变`
home页面中的获取首页信息gethomeInfo根据城市的变化而重新请求,当选择城市不变时,不需要再次请求
因为有多个组件用到城市,所以把获取城市信息放在store中,另外在store的data中定义当前城市citecity
在store mitations中,利用localStorage,把当前城市赋值给本地存储
````
changecity(state,payload){
try{ //使用try{}catch{}避免因部分浏览器未开启本地缓存服务而报错
localStorage.setItem('site',payload);
state.sitecity=localStorage.getItem('site');
}catch(e) {
console.log(e);
}
//state.sitecity=payload;
},
````
在data中,这只默认城市,以及获取当前缓存城市
````
sitecity:localStorage.getItem('site')||'北京',
````
在城市列表页,cityList.vue中
````
movecity(city){
// console.log('111');
if(city!==this.sitecity){
//避免相同城市二次请求 应该写在另一个请求上
//城市不一样时 状态state变成false
this.changecity(city);
}
this.$router.push('/')
}
````
点击热门城市时,触发movecity方法 cityList --->store(改变城市)-->sitecity改变
当city!==this.sitecity时,也就是说同一城市时,直接跳转到首页(这里可以不用判断 哈哈)
在home页面中
````
created() {
//加上keep-alive 这个函数在初始时走一次 之后在切换路由时,就不走了
//如果没加,则重进页面会再次执行
// console.log('111');
this.getHomeInfo();
},
activated() {
//在页面重新显示时候执行
//把城市换了,再次请求axios 在这写 和监听城市变化 一样啊
},
watch:{
sitecity(){
this.getHomeInfo();
}
}
````
监听sitecity时是否变化,从而判断要不要重新获取城市详情,所以之前那个判断可以不要
4.2.函数节流
利用setTimeout函数设置延后处理后续代码
城市列表页搜索功能 设置100毫秒后执行搜索
//这里还需研究 在用户输入100毫秒后在执行,函数节流 如果用户在100毫秒内输入完自己想要的内容,这个应该用处不大,如果用户、
//在100毫秒内没输入完自己想要的,函数就开始搜索,出来不是自己真正想要的内容,所以再次输入时,应该清除之前的结果
//是不是函数每走一遍就生成一个setTimeOut()函数呢??? 不销可能存在自己的作用域
代码:
````
<div class="down">
<input type="text" placeholder="请输入城市名或拼音" v-model="content"></input>
</div>
<div class="search" v-show="content">
<ul>
<li v-for="iteminlist" :key="item.id">{{item.name}}</li>
</ul>
</div>
````
````
content(val){
//当用户输入时 实时触发这个函数
this.list=[];
if(time){
clearTimeout(time);
}
let time=setTimeout(()=>{
`//双重循环查找数组`
Object.values(this.citylist).forEach(v=>{
//循环查找数组
v.forEach(r=>{
if(r.spell.indexOf(val)>-1||r.name.indexOf(val)>-1){
this.list.push(r)
}
})
});
//console.log(this.list);
// if(!this.list.length){
// // let e=document.createElement('div');
// document.getElementById('a').innerText='未找到';
// }
},100);
}
````
4.3.better-scroll插件的使用
better-scroll优点:
可能是目前最好用的移动端滚动插件
使用步骤:
可看文档介绍 · better-scroll
1.安装插件
npm install better-scroll -s
2.在所需组件中引入
import BScroll from 'better-scroll'
3.使用组件
在dom中的解构
````
<div ref='wrapper'>
<div class='content'>
//你所滚动的内容
</div>
</div>
````
````
注意:滚动区域外要有父级包围(外要有2层,很重要)
````
父级的css样式
````
.wrapper{
position:absolute;
top:1.3rem;
left:0;
right:0;
bottom:0;
overflow:hidden;
}
````
注意:父级一定要有自己的高度,且子集的高度要高于父级,并且加上overflow:hidden
初始化插件
在组件的mounted生命周期钩子函数中:
````
mounted(){
this.$nextTick(()=>{
this.scroll =new BScroll(this.$refs.wrapper,{click:true});
})
}
````
注意:
1.插件初始化需要具体元素,所以在dom树中,用vue ref来确定具体dom节点
2.初始化放在nextTick方法中,避免dom树未渲染完成从而造成子元素(滚动内容)高度计算不准确
出现问题:
点击事件 : 可能因不能用,在配置中加上 {click:true}即可
滚动失效:手机滚动和键盘滚动不一样,本来是可以滚动的 (已解决)
返回顶部:未解决
4.4.递归组件的使用
使用场景:
票价的多级显示
递归组件的应用使用原因:
对于上面的嵌套层级关系,我们完全可以使用循环来操作,但是,我们想一想,如果一天老板要求在水上乐园下面在加一层,二层,甚至更多层,这样一层一层嵌套,循环显然不是明智的算法考虑,所以我们用到了递归组件
定义:
在自己的组件中调用自己
使用说明:
1.个人认为,处理数据时关键一步 嵌套关系的data
```
"list":[
{
"title":"成人票",
"children":[
{
"title":"成人三联馆"
},{
"title":"情侣双人游"
},{
"title":"闺蜜团"
}
]
},{
"title":"老人票",
"children":[
{
"title":"夕阳红旅游团"
},{
"title":"老人三联馆"
}
]
},{
"title":"儿童票",
"children":[{
"title":"亲子游"
},{
"title":"儿童乐园",
"children":[{
"title":"水上游玩"
},{"title":"侠盗猎车空中游"}]
}]}
]
```
2.模板代码处理
组件名:Recurrence
```
<template>
<div>
<div v-for="(item,index) in list" :key="index">
<div @click="activeIndex=index" style="cursor: pointer;border-bottom: .02rem lightblue solid;line-height: .4rem;padding:.1rem 0 ">
<span class="icon-icon-test iconfont" style="display: inline-block;height: .45rem;font-size: .4rem;text-align: center"></span>
<span style="display: inline-block;height: .45rem;font-size: .4rem;text-align: center;color: black;margin-left: .3rem">{{item.title}}</span>
</div>
<div v-if="item.children" v-show="activeIndex===index" class="item">
<Recurrence :list="item.children"></Recurrence>
</div>
</div>
</div>
</template>
```
3.组件有个来自父级的props:['list'],点击每一级会显示出下一级;
4.5.background-image处理渐变
对于css3 opacity 的认识 规定不透明度。从 0.0 (完全透明)到 1.0(完全不透明)
background-image:linear-gradient(角度,颜色) to+方向 指定哪个地方结束
to bottom 就是从顶部到底部的过渡 相当于(180deg,rgba(0,0,0,0),rgba(0,0,0,0.8)
底部有阴影4.6.画廊渐隐渐显效果 vue动画使用
定义一个fade组件,让包含在内的组件,显示或者隐藏有过渡的效果
```
<transition>
<slot></slot>
</transition>
```
```
.v-enter,.v-leave-to{
opacity:0;
}
.v-enter-active,.v-leave-active{
transition:opacity .5s;
}
```
vue动画过渡类```
<Fade>
<Gallery v-show="isHidden" @change="isHidden=!isHidden" :list="gallaryImg"></Gallery>
</Fade>
```
4.6.详情页 下拉显示header,渐显的效果
实现向下滚动详情页时,到了一定高度,头部标题切换效果
```
<div>
<div class="fanh" v-show="isfanh">
<router-link class="iconfont icon-fanhui" to="/" tag="div" style=" font-size: .4rem; line-height: .52rem;"></router-link>
</div>
<div class="header"
v-show="!isfanh"
>
<span>
<router-link class="iconfont icon-fanhui" style="color: white;font-size: .4rem" to="/" ></router-link>
</span>
<span style="margin-left: 2.5rem">景点详情</span>
</div>
</div>
```
利用钩子函数设置监听事件
```
activated() {
//只要页面重新渲染就会执行
//监听事件 只要有下滑时,就会触发事件
document.addEventListener('scroll',this.handelScroll);
},
deactivated() {
//对全局事件解绑 当前页面消失时,执行这个代码
document.removeEventListener('scroll',this.handelScroll)
}
```
通过变量:isfanh控制是否显示
```
handelScroll(){
//如果向下滑动了60 则就会显示header
let top=document.documentElement.scrollTop;
if(top>60){
//头部透明渐变效果
this.isfanh=false
}else{
this.isfanh=true;
}
}
```
animation过渡效果
css代码:
```
.header{
animation:move 3s linear 0s;
}
@keyframes move {
from{
opacity:0;
}to{
opacity:1 }
}
```
4.7.keep-alive
在App.vue中
```
<keep-alive>
<router-view></router-view>
</keep-alive>
```
对于数据的缓存,避免多次重复请求
```
created() {
//加上keep-alive 这个函数在初始时走一次 之后在切换路由时,就不走了
//如果没加,则重进页面会再次执行
// console.log('111');
this.getHomeInfo();
},
```
可以把初始化的请求数据放在created中,把每次进入页面都要出发的放在activited中
保持页面的状态
A——>B——>A 希望返回到A页面时,还是第一次浏览时的位置