vue3搭建实战项目笔记三
vue3搭建实战项目笔记三
- 3.1.行高偏移问题
- 3.2.谷歌浏览器上不能定位
- 3.2.2 移动端css隐藏滚动条
- 3.3.获取列表的数据
- 3.3.1 服务器返回十万条数据
- 3.3.2 分页展示数据
- 3.3.2 防止展示数据为空报错
- 3.4.上拉加载数据
- 3.4.1 加载更多数据
- 3.4.2 监听页面滚动到底部
- 3.4.3 监听滚动的时机
- 3.4.4 监听滚动抽取成hooks
- 3.4.5 useScrollHook在页面中使用
- 3.4.6 监听滚动距离scrollTop
- 3.4.7 优化scrollHook
- 3.5.显示头部搜索
- 3.6 解决手动切换路由不更新tarBar的问题
- 3.7 开始时间和结束的共享
3.1.行高偏移问题
- 3.1.1.引入了normalize.css,引起的行高偏差问题
- 1.产生的原因:- 由于引入了normalize.css, 所以html的line-height: 1.15;
- line-height是会继承的,line-height没有设置单位,相当于是fontSize的倍数
- 例如:
line-height: 2; => 12*2 = 24px font-size: 12px;
- line-height去掉文字的高度后再进行平方,1.15*12 = 13.8
- 去掉文字高度后,只剩下1.8 无法平方,可能有些浏览器会下面来个1,上面来一个0(0.8做一个向下取证Math.floor(0.8))
- 2.防止行高偏差的解决方案:
在assets文件夹下的css目录下的reset.css进行html行高的重置
```css
html {
/* 目前大多数浏览器的line-height:1.2是比较常见的 */
line-height: 1.2;
}
```
- 3.补充:中文的文字会向上偏移一点点,英文反而是有一点点向下沉的
3.2.谷歌浏览器上不能定位
- 1.在Edge浏览器测试定位
Edge浏览器在渲染界面的时候,渲染的引擎对应的chrome里面的Blink是一样的
在实现一些API的时候不一样的 - 2.拿到对应的经纬度发送给服务器(或第三方服务器百度,高德)并返回城市信息
- longitude:113.2643
- latitude: 23.129
- 3.分析谷歌浏览器不能定位的原因:
- 3.1.如果电脑可以连上国外的服务器,就可以获取到对应的定位信息
- 3.2.edge浏览器和chrome浏览器对于geolocation.getCurrentPosition()的实现不一样
- edge:
- 获取当前位置信息,是直接获取下电脑有没有获取到当前用户的位置,如果有获取的到直接返回当前用户位置
- chrome:
- 拿到当前电脑的IP地址,把IP地址发送给谷歌服务器,要求必须先连接上谷歌的服务器,
在国内是连接不上谷歌服务器的,连接不上就没法根据IP地址分析出当前位置,无法返回对应的地址信息 - 手机上是可以正常获取的
- 3.3.解决:
在edge、手机上是可以正常获取到定位信息
- 4.参考文档:https://www.w3.org/TR/geolocation/#introduction
- geolocationAPI是一个高度抽象的接口,就是不告诉内部怎么实现的
- 内部是不管不规定是如何通过IP地址、GPS、RFID、WiFi、蓝牙等设备信息来分析出当前用户的位置信息,只是和设备的速度有关联
- 在谷歌PC端,谷歌是通过IP地址来分析出当前用户的位置信息,不保证返回的一定是当前设备的真实位置
- 5.补充:sdk - software development kit
- 完整代码:
navigator.geolocation.getCurrentPosition((renderToString) => { // latitude// 纬度 // longitude// 经度 console.log('获取位置成功:', renderToString); }, (res) => { console.log('获取位置失败:', res); },{ enableHighAccuracy: true, timeout: 5000, maximumAge: 0, })
3.2.2 移动端css隐藏滚动条
::webkit-scrollbar {
display: none;
}
3.3.获取列表的数据
3.3.1 服务器返回十万条数据
-
- 在当前应用程序里开一个对应的线程(多线程的东西-webworker)
-
- 虚拟列表,比较少见
-
- 一般在整个项目架构的时候不推荐返回十万条数据,除非非常需要,最好不要放在前端处理十万条数据,前端出处理十万条数据,会让浏览器变得非常卡,对用户体验不好,让服务器去处理去占用对应的资源,让前端来做展示(用户浏览器占用过多资源的话,会造成卡顿,体验不好,最好不要这么做)
- 两种做法如下:
- 引入第三方库 axios,直接在组件页面直接使用axios发送网络请求,但是这样来做不好,不利于代码维护
- 最好对axios做一层封装,不管是复用性、可用性、可维护性,甚至框架的替换都是更方便的
3.3.2 分页展示数据
- 1.分页请求传参
页码和每页条数 page, size (服务器有做分页的情况下)
每页条数和偏移 size offset=0,30,60 (服务器没有做分页的情况下)
3.3.2 防止展示数据为空报错
- 1.使用可选链?.
```javascript
const { userInfo } = getUserInfo()
console.log(userInfo?.image?.url)
```
- 2.使用v-if
```html
<div v-if="userInfo.image">{{userInfo.image.url}}</div>
```
3.4.上拉加载数据
3.4.1 加载更多数据
- 1.在homeStore中定义一个页码来记录当前第几页,默认为第一页
- 2.每次点击加载更多,发送请求,请求成功后, 把数据添加到数组中,然后重新渲染页面, 然后页码+1。
- 3.代码如下:
```javascript
// 1.service.js
export function getHomeCategories () {
return hyRequest.get({ url: "/home/categories" })
}
// 2.homeStore.js
import { defineStore } from "pinia";
import { getHomeHouseList} from "@/service";
const useHomeStore = defineStore("home", {
state: () => ({
currentPage: 1,
houselist: []
}),
actions: {
async fetchHouseListData () {
const res = await getHomeHouseList(this.currentPage)
this.houselist.push(...res.data)
this.currentPage++
}
}
})
export default useHomeStore
```
3.4.2 监听页面滚动到底部
- 1. 页面为什么会滚动
- 1.1.window窗口滚动(整个页面所有内容超过100%并且有滚动条)
- 1.2.元素滚动(元素设置overflow-y: auto)
- 2.监听滚动
```javascript
// window窗口的滚动
window.addEventListener('scroll', () => {})
// 元素的滚动
divEl.addEventListener('scroll', () => {})
```
- 3.监听滚动到底部
- 3.1.想要知道滚动了多少区域
- 3.1.1. 它其实是我们对应文档的一个滚动,整个窗口之所以可以滚动,是因为它的文档变得非常的长,当往上面滚动的时候,有一个往上滚动的高度
- 3.1.2. 往上滚动的高度叫做scrollTop
- 3.2.怎么知道滚动到了底部
- 3.2.1. 可以拿到整个文档可以滚动的区域scrollHeight(也相当于整个文档的高度)
- 3.2.2. 拿到客户端的高度,如果客户端的高度 + scrollTop >= scrollHeight 说明滚动到底部
- 3.3.代码如下:
```javascript
// 监听窗口的滚动
window.addEventListener('scroll', () => {
const clientHeight = document.documentElement.clientHeight
const scrollTop = document.documentElement.scrollTop
const scrollHeight = document.documentElement.scrollHeight
if (clientHeight + scrollTop >= scrollHeight) {
// 如果说到达底部了,就加载更多数据
homeStore.fetchHouseListData()
}
})
```
3.4.3 监听滚动的时机
- 1. 当加载页面时,监听滚动
```javascript
onMounted(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
```
- 2. 当离开页面时,移除监听滚动(原因:页面之间都监听了window的滚动,会影响下一个页面的监听滚动,所以需要再卸载页面移除滚动事件)
```javascript
onUnmounted(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
```
3.4.4 监听滚动抽取成hooks
- 在hooks中封装一个useScroll(原因:别的页面也需要进行类似的监听)
- 1.1.判断滚动到底部的两种做法:
1. 传入一个回调函数,一旦滚动到底部执行回调函数
```javascript
import { ref, onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'
export default function useScroll(reachBottomCB) {
const scrollListenerHandler = () => {
const clientHeight = document.documentElement.clientHeight
const scrollTop = document.documentElement.scrollTop
const scrollHeight = document.documentElement.scrollHeight
if (clientHeight + scrollTop >= scrollHeight) {
if(reachBottomCB) {
reachBottomCB()
}
}
}
onMounted(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onUnmounted(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
onActivated(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onDeactivated(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
}
```
2. 返回一个变量,代表是否滚动到底部
原因:回调比较多的时候,不好管理,更好管理的是变量
```javascript
import { ref, onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'
export default function useScroll() {
const isReachBottom = ref(false)
const scrollListenerHandler = () => {
const clientHeight = document.documentElement.clientHeight
const scrollTop = document.documentElement.scrollTop
const scrollHeight = document.documentElement.scrollHeight
if (clientHeight + scrollTop >= scrollHeight) {
isReachBottom.value = false
}
}
onMounted(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onUnmounted(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
onActivated(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onDeactivated(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
return {
isReachBottom
}
}
```
3.4.5 useScrollHook在页面中使用
- 3.4.2.4.1. 回调函数形式
```javascript
import useScroll from '@/hooks/useScroll';
// 监听是否滚动底部
useScroll(() => {
homeStore.fetchHouseListData()
})
```
- 3.4.2.4.2. 返回一个变量
```javascript
import useScroll from '@/hooks/useScroll';
// 拿到isReachBottom,然后监听isReachBottom为真的时候滚动到底部
const { isReachBottom } = useScroll()
// 监听是否滚动底部
watch(isReachBottom, (newValue) => {
if(newValue) {
homeStore.fetchHouseListData().then(() => {
isReachBottom.value = false
})
}
})
```
3.4.6 监听滚动距离scrollTop
- 1.局部监听滚动距离放到全局上,返回把滚动距离返回出去
- 2. 代码如下:
```javascript
import { ref, onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'
export default function useScroll() {
const isReachBottom = ref(false)
const clientHeight = ref(0) // 可见的高度
const scrollTop = ref(0) // 滚动的距离
const scrollHeight = ref(0) // 整个文档的可滚动的高度
// 监听窗口的滚动
const scrollListenerHandler = (reachBottomCB) => {
clientHeight.value = document.documentElement.clientHeight
scrollTop.value = document.documentElement.scrollTop
scrollHeight.value = document.documentElement.scrollHeight
if (clientHeight.value + scrollTop.value >= scrollHeight.value) {
isReachBottom.value = true
}
}
onMounted(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onUnmounted(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
onActivated(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onDeactivated(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
return {
isReachBottom,
clientHeight,
scrollTop,
scrollHeight,
}
}
```
3.4.7 优化scrollHook
- 使用使用underscore库节流优化scrollHook
- 1.监听滚动时,发现函数执行的次数很频繁,使用节流处理,减少函数执行次数。
- 2.使用underscore.js中的throttle函数,对scrollHook进行优化。
- 3.安装underscore: npm install underscore
- 4.使用:import { throttle } from 'underscore'
```javascript
import { ref, onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'
import { throttle } from 'underscore'
// 执行频率太高使用:防抖/节流
// 防抖:本来准备去执行一个东西,但是发现又触发了,然后把这个事件不断的延后,直到最后一次才执行
// 节流:在单位时间内执行一个函数是非常频繁的,规定一个时间执行一次函数,每间隔一个时间执行一次函数
export default function useScroll() {
const isReachBottom = ref(false)
const clientHeight = ref(0) // 可见的高度
const scrollTop = ref(0) // 滚动的距离
const scrollHeight = ref(0) // 整个文档的可滚动的高度
const scrollListenerHandler = throttle(() => {
clientHeight.value = document.documentElement.clientHeight
scrollTop.value = document.documentElement.scrollTop
scrollHeight.value = document.documentElement.scrollHeight
if (clientHeight.value + scrollTop.value >= scrollHeight.value) {
isReachBottom.value = true
}
}, 100)
onMounted(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onUnmounted(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
onActivated(() => {
window.addEventListener('scroll', scrollListenerHandler)
})
onDeactivated(() => {
window.removeEventListener('scroll', scrollListenerHandler)
})
return {
isReachBottom,
clientHeight,
scrollTop,
scrollHeight,
}
}
```
3.5.显示头部搜索
-3.4.3.7.滚动到100显示头部搜索框
```javascript
import { watch, ref, computed } from ‘vue’;
import useScroll from ‘@/hooks/useScroll’;
const { isReachBottom, scrollTop } = useScroll()
// const isShowSearchBar = ref(false)
// 1.监听scrollTop的距离
// watch(scrollTop, (newScrollTop) => {
// isShowSearchBar.value = newScrollTop > 100
// })
// 当一个可响应式数据依赖(影响)另外一个可响应式数据的时候,使用计算函数
const isShowSearchBar = computed(() => {
return scrollTop.value >= 100
})
```
3.6 解决手动切换路由不更新tarBar的问题
```javascript
// 1.开启路由模式
<template>
<van-tabbar v-model="currentIndex" active-color="#ff9854" route></van-tabbar>
</template>
// 2.监听路由变化,找到对应的索引,并修改currentIndex
import { ref, watch } from 'vue';
const route = useRoute()
watch(route, (newRoute) => {
const index = tabbarData.findIndex(item => item.path === newRoute.path)
if(index === -1) return
currentIndex.value = index
})
```
3.7 开始时间和结束的共享
```javascript
// 抽取到一个公共的mainStore中
// src/store/main.js
import { defineStore } from "pinia";
const startDate = new Date()
const endDate = new Date()
endDate.setDate(startDate.getDate()+1) // 或者 nowDate.getTime() + 24*60*60*1000
const useMainStore = defineStore('main', {
state: () => ({
token: '',
startDate: startDate,
endDate: endDate
})
})
export default useMainStore
// src/views/cpns/home-search-box.vue
// 日期范围处理(页面中使用)
import { computed, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { formatMonthDay, getDiffDays } from '@/utils/format_date';
import useMainStore from '@/stores/modules/main';
const calendarShow = ref(false)
const mainStore = useMainStore()
const { startDate, endDate} = storeToRefs(mainStore)
// formatMonthDay格式日期,默认格式为MM月DD日
const startDateStr = computed(() => formatMonthDay(startDate.value))
const endDateStr = computed(() => formatMonthDay(endDate.value))
const stayCount = ref(getDiffDays(startDate.value, endDate.value))
const onConfirm = (value) => {
// 1.设置日期
const selectStartDate = value[0]
const selectEndDate = value[1]
mainStore.startDate = selectStartDate
mainStore.endDate = selectEndDate
stayCount.value = getDiffDays(selectStartDate, selectEndDate)
// 2.隐藏日历
calendarShow.value = false
}
// src/components/search-bar.vue
// 页面中使用
import useMainStore from '@/stores/modules/main';
import { formatMonthDay } from '@/utils/format_date';
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
const mainStore = useMainStore()
const { startDate, endDate } = storeToRefs(mainStore)
const startDateStr = computed(() => formatMonthDay(startDate.value, 'MM.DD'))
const endDateStr = computed(() => formatMonthDay(endDate.value, 'MM.DD'))
```