前端Vue项目:旅游App-(17)home:页面滚动显示搜索栏节流时间同步

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端Vue项目:旅游App-(17)home:页面滚动显示搜索栏节流时间同步相关的知识,希望对你有一定的参考价值。

文章目录

本项目博客总结:【前端】Vue项目:旅游App-博客总结

目标

窗口滚动到固定位置时显示搜索栏:


且搜索栏左侧显示的时间与入住离开的时间相匹配。

过程与代码

页面滚动到目标位置显示搜索框

我们可以用v-if控制搜索框的显示与否,用上篇写的useScroll得知页面的滚动情况。

为了useScroll的扩展性,我们可以把scrollHeightscrollTopclientHeightreturn出来:

// 关于滚动到底部的代码逻辑
import  onMounted, onUnmounted  from "@vue/runtime-core";
import  ref  from 'vue'

export default function useScroll() 
    // 初始默认为没有到底
    const isReachBottom = ref(false)
    const scrollTop = ref(0)
    const clientHeight = ref(0)
    const scrollHeight = ref(0)

    const scrollBottomListener = () => 
        // 当前位置到顶部的距离
        scrollTop.value = document.documentElement.scrollTop
        // 屏幕的长度
        clientHeight.value = document.documentElement.clientHeight
        // 页面总体长度
        scrollHeight.value = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight.value <= scrollTop.value + clientHeight.value + 1) 
            console.log('滚动到底部')
            isReachBottom.value = true
        
    

    onMounted(() => 
        window.addEventListener('scroll', scrollBottomListener)
    )

    onUnmounted(() => 
        window.removeEventListener('scroll', scrollBottomListener)
    )

    return  isReachBottom, scrollHeight, clientHeight, scrollTop 

在需要知道当前页面滚动到哪里的时候把scrollTop 解构出来即可。

需求:在页面滚动到开始搜索按钮时显示搜索栏(严谨地说,是此按钮刚好被上滑的页面遮盖住时显示)。因此我们可以:

scrollBottomListener是窗口滚动时会启动的事件,我们这样就可以实时监听到scrollTop 的变化。

浏览器控制台输出“开始搜索按钮刚好被遮盖一点”时页面距离窗口顶部的距离为:484


watch监听scrollTop ,当它>=484时令搜索框显示。

html

<div class="search-bar" v-if="isShowSearchBar">
   我是搜索框
</div>

js:

// 是否显示搜索栏的控制
const isShowSearchBar = ref(false)
const  scrollTop  = useScroll()
watch(scrollTop, (newValue) => 
    if (scrollTop.value >= 484) 
        isShowSearchBar.value = true
    
    else 
        isShowSearchBar.value = false
    
)

用计算属性优化:

定义的可响应数据依赖于另一个可响应数据,可以使用计算属性

const isShowSearchBar = computed(() => 
    return scrollTop.value >= 484
)

效果:

有了一点遮挡时,显示:


反之没有:

优化:节流

相关资料:
面试官:什么是防抖和节流?有什么区别?如何实现? | web前端面试 - 面试官系列 (vue3js.cn)
Underscore.js 简介 | Underscore.js 中文文档 | Underscore.js 中文网 (underscorejs.cn)

我们观察useScroll函数,每当窗口滚动时,都会调用回调函数scrollBottomListener
它调用函数十分频繁,会降低前端的性能。因此,我们要对它进行优化。

优化有两种主要方式:防抖和节流。

定义:

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

应用场景:

防抖在连续的事件,只需触发一次回调的场景有:

搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流在间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部
监听搜索框,搜索联想功能

这里很明显要使用节流

我们可以使用已经封装好的库来调用节流的函数。

npm i underscore


第一个参数是要进行节流的函数,这里是scrollBottomListener
第二个参数是再次执行函数要间隔的时间。

时间设为100ms刚好,若设置为1000ms会明显感觉到延迟。注意,wait不要设置太大。

const scrollBottomListener = throttle(() => 
        // 当前位置到顶部的距离
        scrollTop.value = document.documentElement.scrollTop
        // 屏幕的长度
        clientHeight.value = document.documentElement.clientHeight
        // 页面总体长度
        scrollHeight.value = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight.value <= scrollTop.value + clientHeight.value + 1) 
            console.log('滚动到底部')
            isReachBottom.value = true
        

        // console.log(scrollTop.value)
    , 100)

搜索栏

想要在固定位置有个搜索栏,要position:fixed

html:

<div class="search-bar" v-if="isShowSearchBar">
    <searchBar/>
</div>

css:

.search-bar
   // 定位在屏幕某位置,不随页面滚动而改变
   position: fixed;
   top: 0;
   left: 0;
   right: 0;

   height: 45px;

   background-color: #fff;
   // 防止被house-item组件中有绝对定位的覆盖掉
   z-index: 9;

对应searchBar组件:

<template>
    <div class="search">
        <div class="left">
            <div class="item start">
                <div class="name"></div>
                <div class="time">02.01</div>
            </div>
            <div class="item end">
                <div class="name"></div>
                <div class="time">02.02</div>
            </div>
        </div>
        <div class="content">
            <div class="keyword">关键字/位置/民宿</div>
        </div>
        <div class="right">
            <van-icon size="20px" color="#3f4954" name="search" />
        </div>
    </div>
</template>

<script setup>


</script>

<style lang="less" scoped>
.search 
    display: flex;
    justify-content: start;
    align-items: center;
    border-radius: 10px;
    background-color: #F5F5F5;
    margin: 5px 15px 10px;
    // padding: 5px;

    .left 
        display: flex;
        flex-direction: column;
        margin-left: 10px;

        .item 
            display: flex;
            flex-direction: row;
            font-size: 12px;
            margin: 3px;
        

        .name 
            color: #999;
        

        .time 
            margin: 0 3px;
            color: #000;
        

    

    .content 
        width: 80%;
        margin: 0 10px;

        .keyword 
            color: #999;
            font-size: 13px;
        
    

    .right 
        margin-right: 15px;
    

</style>

效果:

显示时间同步

想让显示时间同步,显然我们要把时间放在store中统一管理。在日历中修改时间即在store中修改时间。任何需要读取时间的地方都在store中读取。

import  defineStore  from "pinia"

const startDay = new Date()
const endDay = new Date()
endDay.setDate(startDay.getDate() + 1)

const useMainStore = defineStore('main', 
    state: () => (
        token:'',

        startDay:startDay,
        endDay:endDay
    ),
    actions: 

    
)

export default useMainStore

在search-box中将时间同步:

// 日期
const  startDay, endDay  = storeToRefs(mainStore)
const startDayStr=ref(formatMonthDay(startDay.value))
const endDayStr=ref(formatMonthDay(endDay.value))

watch(startDay,(newValue)=>
    startDayStr.value=formatMonthDay(startDay.value)
)
watch(endDay,(newValue)=>
    endDayStr.value=formatMonthDay(endDay.value)
)

// 日历
const date = ref('1');
const showCalendar = ref(false);

const formatDate = (date) => `$date.getMonth() + 1/$date.getDate()`;
const onConfirm = (values) => 
    const [start, end] = values;
    showCalendar.value = false;

    mainStore.startDay=start
    mainStore.endDay=end
    date.value = getDiffDate(start, end)
;

在search-bar中时间同步的操作相似,不赘述。

效果


总代码

修改或添加的文件


search-bar.vue

组件:搜索栏。

<template>
    <div class="search">
        <div class="left">
            <div class="item start">
                <div class="name"></div>
                <div class="time"> startDayStr </div>
            </div>
            <div class="item end">
                <div class="name"></div>
                <div class="time"> endDayStr </div>
            </div>
        </div>
        <div class="content">
            <div class="keyword">关键字/位置/民宿</div>
        </div>
        <div class="right">
            <van-icon size="20px" color="#3f4954" name="search" />
        </div>
    </div>
</template>

<script setup>
import  ref  from 'vue';
import  storeToRefs  from 'pinia';

import useMainStore from '@/store/modules/main';
import  formatMonthDay2  from '@/utils/formatDate'

const mainStore = useMainStore()
const  startDay, endDay  = storeToRefs(mainStore)
const startDayStr = ref(formatMonthDay2(startDay.value))
const endDayStr = ref(formatMonthDay2(endDay.value))

</script>

<style lang="less" scoped>
.search 
    display: flex;
    justify-content: start;
    align-items: center;
    border-radius: 10px;
    background-color: #F5F5F5;
    margin: 5px 15px 10px;
    // padding: 5px;

    .left 
        display: flex;
        flex-direction: column;
        margin-left: 10px;

        .item 
            display: flex;
            flex-direction: row;
            font-size: 12px;
            margin: 3px;
        

        .name 
            color: #999;
        

        .time 
            margin: 0 3px;
            color: #000;
        

    

    .content 
        width: 80%;
        margin: 0 10px;

        .keyword 
            color: #999;
            font-size: 13px;
        
    

    .right 
        margin-right: 15px;
    

</style>

useScroll.js

增加了拓展性和节流。

// 关于滚动到底部的代码逻辑
import  onMounted, onUnmounted  from "@vue/runtime-core";
import  ref  from 'vue'
import  throttle  from "underscore";

export default function useScroll() 
    // 初始默认为没有到底
    const isReachBottom = ref(false)
    const scrollTop = ref(0)
    const clientHeight = ref(0)
    const scrollHeight = ref(0)

    const scrollBottomListener = throttle(() => 
        // 当前位置到顶部的距离
        scrollTop.value = document.documentElement.scrollTop
        // 屏幕的长度
        clientHeight.value = document.documentElement.clientHeight
        // 页面总体长度
        scrollHeight.value = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight.value <= scrollTop.value + clientHeight.value + 1) 
            console.log('滚动到底部')
            isReachBottom.value = true
        

        // console.log(scrollTop.value)
    , 100)

    onMounted(() => 
        window.addEventListener('scroll', scrollBottomListener)
    )

    onUnmounted(() => 
        window.removeEventListener('scroll', scrollBottomListener)
    )

    return  isReachBottom, scrollHeight, clientHeight, scrollTop 

store的main.js

整个项目的时间显示保存在这里。

import  defineStore  from "pinia"

const startDay = new Date()
const endDay = new Date()
endDay.setDate以上是关于前端Vue项目:旅游App-(17)home:页面滚动显示搜索栏节流时间同步的主要内容,如果未能解决你的问题,请参考以下文章

前端Vue项目:旅游App-(13)home:热门数据的网络请求store和显示

前端Vue3+Vant4项目:旅游App-项目总结与预览(已开源)

前端Vue项目:旅游App-(14)home+search:搜索按钮及其路由跳转分组数据的网络请求request数据存储store和动态显示

前端Vue项目:旅游App-(14)home+search:搜索按钮及其路由跳转分组数据的网络请求request数据存储store和动态显示

前端Vue项目:旅游App-(20)home:点击跳转至带参数的动态路由

前端Vue项目:旅游App-(16)home+hooks:窗口滚动到底部动态加载新数据抽取到hook