vue3滚动条滚动后元素固定
代码地址:https://gitee.com/zzhua195/easyblog-web-vuee

Framework.vue
在这个布局组件中,监听main的滚动事件,获取滚动的距离,将它存入store,以便其它组件能够共享,监听到
<template><div class="header"><div class="header-wrapper"><Header></Header></div></div><div class="main" ref="mainRef"><router-view/><div class="main-footer" v-if="route.fullPath === '/blog' || route.fullPath === '/member'"><div class="content-wrapper"><Footer></Footer></div></div></div>
</template><script setup>import Header from './Header.vue';import Footer from './Footer.vue';import { onMounted,ref } from 'vue';import { useStore } from 'vuex'import store from '@/store';import { useRoute } from 'vue-router'const route = useRoute()const mainRef = ref(null)onMounted(()=>{// console.log(mainRef.value);mainRef.value.addEventListener('scroll',function(){store.commit('updateMainScrollTop',mainRef.value.scrollTop)})})</script><style lang="scss"></style>
Blog.vue
- 先给main-body-right动态绑定sitcky的类名,在这个类名下,先设置好固定定位的样式
- 监听store里的滚动距离,如果大于这个距离,就给main-body-right添加sticky这个类名,如果小于就删除这个类名
- 第三步也是最麻烦的,我们需要动态的获取到teamUserRef这个dom距离它的定位父元素的高度,不想写死这个高度。有几个坑踩过了:
- 不能直接在onMounted中获取mainRef距离它最近的定位父元素的高度,因为onMounted是在组件挂载完后执行,注意到,在setup里面,是通过promise发的异步请求,数据还没返回来,这个时候setup已经执行完了,用的是定义的最初的数据就进行了渲染
- 不能直接在then的回调中直接去拿mainRef距离它最近的定位父元素的高度,因为(js单线程),等到数据回来了(事件循环任务队列),执行完then回调后(整个then执行完),再执行的渲染,应该是要等到到渲染完毕,才能获取到这个距离,要放到nextTick里面才行。
- 因为在setup里面有获取专栏和专题这2个数据,也不知道谁先谁后,所以两个地方都写吧
- mainRef刚开始是直接写在BlogItem组件上的,发现拿到的是undefined,是因为vue3需要使用defineExpose把组件需要的东西暴露出来,才能拿到,拿到的也不是dom。我以为拿到的是dom,这是错的!
- 也可以监听BlogItem的finished事件,来获取这个高度,但是触发事件时也必须写在onUpdated里面,
- 下面是执行的打印情况,也可以从中看出vue它的执行过程,可以看到三个子组件先onMounted了,然后父组件就onMounted了,然后专栏的数据响应回来了,先把回调全部执行完(专栏同步),然后触发子组件更新,updated回调,然后nextTick就执行了,专题也是一样。



<template><div class="main-body content-wrapper" :style="{ 'min-height': 'calc(100% - 10px)' }"><div class="main-body-left"><BlogItem :blogInfo="blogInfo" v-for="blogInfo, index in blogDataInfo.list" :key="index" class="blog-info" /><el-pagination background layout="prev, pager, next" style="margin-bottom: 12px;":total="blogDataInfo.totalCount" @current-change="handleCurrentChange" :pageSize="blogDataInfo.pageSize" /></div><div :class="['main-body-right', { 'sticky': isSticky }]"><div><blog-side-item who="zhuanlan" @finished="finishd" title="分类专栏"><ul><li class="li-side-category" v-for="category, index in categoryListInfo" :key="index"><div class="side-category"><img class="avatar" :src="proxy.globalInfo.imageUrl + category.cover" alt=""><router-link :to="'/category/' + category.categoryId"><h2>{{ category.categoryName }}</h2></router-link></div><div class="count"><span>{{ category.blogCount }}篇</span></div></li></ul></blog-side-item></div><div><blog-side-item who="zhuanti" @finished="finishd" title="专题"><ul><li class="li-side-category" v-for="special, index in specialInfo.list" :key="index"><div class="side-category"><img class="avatar" :src="proxy.globalInfo.imageUrl + special.cover"><router-link :to="'/special/' + special.categoryId"><h2>{{ special.categoryName }}</h2></router-link></div><div class="count"><span>{{ special.blogCount }}篇</span></div></li></ul></blog-side-item></div><div style="transition:top 1s;" ref="teamUserRef"><blog-side-item who="team-user" title="博客成员" class="team-user" id="teamUser" ><ul><li v-for="userInfo, index in userListInfo" :key="index" class="user-li"><router-link :to="'/member#user-info-' + userInfo.userId" href="#" class="user-part"><img :src="proxy.globalInfo.imageUrl + userInfo.avatar" alt=""><div class="user-info"><div class="nick-name">{{ userInfo.nickName }}</div><div class="profession">{{ userInfo.profession }}</div></div></router-link><div class="count"><span>{{ userInfo.blogCount }}</span></div></li></ul></blog-side-item></div></div></div>
</template><script setup>
import { ref, reactive, getCurrentInstance, onMounted, onBeforeUnmount, watch, nextTick, onUpdated } from 'vue'
import BlogItem from './BlogItem.vue'
import BlogSideItem from './BlogSideItem.vue';
import Footer from '@/views/Footer.vue';
import { useStore } from 'vuex'
const store = useStore()
const { proxy } = getCurrentInstance()const topLength = ref(500)const api = {loadBlogList: "/view/loadBlogList",loadCategory: "/view/loadCategory",loadUser: "/view/loadTeamUser",loacSpecial: "/view/loadSpecial",
};const blogDataInfo = ref({pageSize: 10,pageNo: 1
})async function loadBlogList() {let result = await proxy.Request({url: api.loadBlogList,params: {pageSize: blogDataInfo.value.pageSize,pageNo: blogDataInfo.value.pageNo}})blogDataInfo.value = result
}
loadBlogList()
function handleCurrentChange(pageNo) {blogDataInfo.value.pageNo = pageNoloadBlogList()
}const categoryListInfo = ref({})
async function loadCategoryList() {let result = await proxy.Request({url: api.loadCategory,params: {pageSize: 5}})categoryListInfo.value = result/* topLength.value = teamUserRef.value.offsetTopconsole.log(teamUserRef.value.offsetTop, 1, '专栏'); */console.log('专栏同步');nextTick(()=>{topLength.value = teamUserRef.value.offsetTopconsole.log(teamUserRef.value.offsetTop, 1, '专栏');})
}
loadCategoryList()const userListInfo = ref([])async function loadUserInfoList() {let result = await proxy.Request({url: api.loadUser,params: {pageSize: 5}})userListInfo.value = result
}
loadUserInfoList()const specialInfo = ref([])async function loadSpecialList() {let result = await proxy.Request({url: api.loacSpecial,params: {pageSize: 3}})specialInfo.value = result/* topLength.value = teamUserRef.value.offsetTopconsole.log(teamUserRef.value.offsetTop, 2, '专题'); */console.log('专题同步');nextTick(()=>{topLength.value = teamUserRef.value.offsetTopconsole.log(teamUserRef.value.offsetTop, 2, '专题'); })
}
loadSpecialList()const isSticky = ref(false)
let val = null
const teamUserRef = ref(null)watch(() => store.state.mainScrollTop, (newVal, oldVal) => {// console.log('监听到: ' + newVal);if (newVal >= topLength.value) {isSticky.value = true} else {isSticky.value = false}
}, { immediate: true })onMounted(()=>{console.log(teamUserRef.value,'Blog.vue组件onMounted');
})function finishd(who) {topLength.value = teamUserRef.value.offsetTopconsole.log(teamUserRef.value.offsetTop, 'finished回调', who);
}onUpdated(()=>{console.log('Blog.vue组件更新了');
})
</script><style lang="scss" scoped>
.li-side-category {height: 52px;display: flex;justify-content: space-between;align-items: center;.count {margin-left: 5px;flex-shrink: 0;}.side-category {flex: 1;/* 开启省略的关键或者使用width:0 */overflow: hidden;display: inline-flex;align-items: center;.avatar {width: 40px;height: 40px;border-radius: 5px;margin-right: 5px;}a {text-overflow: ellipsis;white-space: nowrap;/* white-space属性为nowrap时,不会因为超出容器宽度而发生换行 */overflow: hidden;h2 {font-weight: normal;margin: 0;font-size: 16px;color: #1890ff;display: inline;}}}
}.user-li {padding: 10px 5px;display: flex;justify-content: space-between;align-items: center;.count {flex-shrink: 0;margin-left: 10px;}.user-part {display: inline-flex;align-items: center;flex-grow: 1;overflow: hidden;img {width: 50px;height: 50px;border-radius: 50%;margin-right: 10px;}.user-info {overflow: hidden;.nick-name {font-size: 20px;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;}.profession {text-overflow: ellipsis;white-space: nowrap;overflow: hidden;color: #8d8d8d;font-size: 12px;}}}}.sticky .team-user {position: fixed;top: 90px;
}</style>
BlogItem.vue
<template><div class="side-item"><div class="side-item-top"><div class="title">{{ title }}</div><div class="more"><a href="#" title="更多" class="iconfont icon-gengduo1"></a></div></div><div class="side-item-body"><slot><div class="no-data">暂无数据</div></slot></div></div>
</template><script setup>
import { ref,reactive,onMounted,onUpdated } from 'vue'
const props = defineProps(['title','who'])
const emit = defineEmits(['finished'])
onMounted(()=>{console.log(props.who, 'onMounted');
})
onUpdated(()=>{console.log(props.who, 'updated');emit('finished',props.who)
})
defineExpose({'halo':'你好吗'
})
</script><style lang="scss">
.side-item {width: 300px;box-sizing: border-box;background-color: #fff;border-radius: 5px;padding: 8px;box-shadow: 0 3px 10px 0px rgb(0 0 0 / 10%);margin-bottom: 10px;.more a {color: #1890ff;font-size: 20px;}.side-item-top {height: 32px;line-height: 32px;display: flex;padding: 0 5px;justify-content: space-between;border-bottom: 1px solid #ddd;}.side-item-body {.no-data {color: #9a9a9a;height: 120px;display: flex;justify-content: center;align-items: center;}}
}
</style>
相关文章:
vue3滚动条滚动后元素固定
代码地址:https://gitee.com/zzhua195/easyblog-web-vuee Framework.vue 在这个布局组件中,监听main的滚动事件,获取滚动的距离,将它存入store,以便其它组件能够共享,监听到 <template><div c…...
新吲哚菁绿染料IR-825 NHS,IR825 NHS ester,IR825 SE,IR-825 活性酯,用于科研实验研究和临床
IR825 NHS理论分析:中文名:新吲哚菁绿-琥珀酰亚胺酯,IR-825 琥珀酰亚胺酯,IR-825 活性酯英文名:IR825 NHS,IR-825 NHS,IR825 NHS ester,IR825 SECAS号:N/AIR825 NHS产品详…...
GO语言--接口(interface)的定义及使用
接口定义 接口也是一种数据类型,它代表一组方法的集合。 接口是非侵入式的。即接口设计者无需知道接口被哪些类型实现,而接口使用者只需知道实现怎样的接口,并且无须指明实现哪一个接口。编译器在编译时就会知道哪个类型实现哪个接口&#…...
【Python语言基础】——Python MongoDB 查询
Python语言基础——Python MongoDB 查询 文章目录 Python语言基础——Python MongoDB 查询一、Python MongoDB 查询一、Python MongoDB 查询 筛选结果 在集合中查找文档时,您能够使用 query 对象过滤结果。 find() 方法的第一个参数是 query 对象,用于限定搜索。 实例 查找地…...
第十四届蓝桥杯模拟赛【第三期】Python
1 进制转换 问题描述 请找到一个大于 2022 的最小数,这个数转换成十六进制之后,所有的数位(不含前导 0)都为字母(A 到 F)。 请将这个数的十进制形式作为答案提交。 答案:2730 def ch…...
windows 下docker 安装clickhouse
docker 下载https://www.docker.com/products/docker-desktop/将下载下来的Docker Desktop Installer.exe文件双击进行安装即可,安装完成后,任务栏会出现一个蓝色的小鲸鱼图标(注意安装完成后可能会重启系统)Docker Desktop如果出…...
【华为OD机试真题 JAVA】TLV编码问题
标题:TLV编码问题 | 时间限制:1秒 | 内存限制:262144K | 语言限制:不限 TLV编码是按TagLengthValue格式进行编码的,一段码流中的信元用tag标识,tag在码流中唯一不重复,length表示信元value的长度,value表示信元的值,码流以某信元的tag开头,tag固定占一个字节,lengt…...
深度学习 Day26——使用Pytorch实现猴痘病识别
深度学习 Day26——使用Pytorch实现猴痘病识别 文章目录深度学习 Day26——使用Pytorch实现猴痘病识别一、前言二、我的环境三、前期工作1、设置GPU导入依赖项2、导入猴痘病数据集3、划分数据集四、构建CNN网络五、训练模型1、设置超参数2、编写训练函数3、编写测试函数4、正式…...
redis简单介绍
对于一名前端工程师,想要进阶成为全栈工程师,redis技术是我们一定需要掌握的。作为当前非关系型数据库Nosql中比较热门的key-value存储系统,了解redis的原理和开发是极其重要的。本文我会循序渐进的带领大家一步步认识redis,使用r…...
Understanding services:理解服务(Service)
文章目录背景1. 准备工作2. ros2 service list 命令3. ros2 service type 命令3.1 ros2 service list -t 命令4. ros2 service find 命令5. ros2 interface show 命令6. ros2 service call 命令参考官方文档: Understanding services背景 服务(Service&…...
【链表OJ题(五)】合并两个有序链表
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:数据结构 🎯长路漫漫浩浩,万事皆有期待 文章目录链表OJ题(五)1. 合并…...
C++ Primer第五版_第三章习题答案(1~10)
文章目录练习3.1练习3.2一次读入一行一次读入一个词练习3.3练习3.4大的字符串长度大的字符串练习3.5未隔开的隔开的练习3.6练习3.7练习3.8练习3.9练习3.10练习3.1 使用恰当的using 声明重做 1.4.1节和2.6.2节的练习。 // 1.4.1 #include <iostream>using std::cin; using…...
小样本学习
机器学习就是从数据中学习,从而使完成任务的表现越来越好。小样本学习是具有有限监督数据的机器学习。类似的,其他的机器学习定义也都是在机器学习定义的基础上加上不同的限制条件衍生出来。例如,弱监督学习是强调在不完整、不准确、有噪声、…...
python打包成apk界面设计,python打包成安装文件
大家好,给大家分享一下如何将python程序打包成apk文件,很多人还不知道这一点。下面详细解释一下。现在让我们来看看! 1、如何用python制作十分秒加减的apk 如何用python制作十分秒加减的apk?用法:. apk包放入apk文件目录,然后输入…...
pytorch转onnx踩坑日记
在深度学习模型部署时,从pytorch转换onnx的过程中,踩了一些坑。本文总结了这些踩坑记录,希望可以帮助其他人。 首先,简单说明一下pytorch转onnx的意义。在pytorch训练出一个深度学习模型后,需要在TensorRT或者openvin…...
极智AI | GPT4来了,ChatGPT又该升级了
欢迎关注我,获取我的更多经验分享 大家好,我是极智视界,本文介绍一下 GPT4来了,ChatGPT又该升级了,更多的是个人思考。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码下载,链接:https://t.zsxq.com/0aiNxERDq 从 ChatGPT 发布 (2022年11月30日) 到…...
智能优化算法之灰狼优化算法(GWO)的实现(Python附源码)
文章目录一、灰狼优化算法的实现思路1、社会等级结构分级2、包围猎物3、攻击猎物4、搜索猎物二、算法步骤三、实例一、灰狼优化算法的实现思路 灰狼优化算法(Grey Wolf Optimizer,简称GWO)是由Seyedali Mirjalili等人于2014年提出的一种群智…...
leetCode热题10-15 解题代码,思路
前言 计划做一系列算法题的文章,因为自己这块确实比较薄弱,但又很重要!写这篇文章前,我已经刷了一本剑指offer,leetcode top150道,牛客某题库106道 这个样子吧,感觉题量算是入门了吧࿱…...
同步辐射GISAXS和GIWAXS的原理及应用领域
同步辐射GISAXS和GIWAXS是两种常用的同步辐射X射线衍射技术,它们在材料科学、化学、生物学、物理学等领域中广泛应用。本文将从原理、实验方法和应用三个方面,对同步辐射GISAXS和GIWAXS进行描述和比较。 一、原理 GISAXS和GIWAXS都是利用X射线与样品相互…...
OpManager 进行网络性能管理
计算机网络构成了任何组织的 IT 基础架构的支柱。由于企业严重依赖基于互联网的应用程序,由于网络相关问题,最终用户不受影响非常重要。因此,借助网络管理解决方案监控和提高网络性能对于保持企业始终正常运行至关重要。这将确保维护服务级别…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
