小程序图片加载优化方案

news2026/3/16 2:35:26
一、背景背景小程序在加载的时候容易出现图片加载缓慢的问题项目图片使用现状分析1. 图片类型与来源类型来源处理方式静态资源图片baseImgUrl 相对路径服务器静态资源OSS图片后端返回的阿里云OSS地址已添加processOssImage自动转webp服务器图片BASE_FILEURL 文件名editFormatFileUrl方法拼接二、性能瓶颈识别1. 主要问题问题影响现状修复后DNS解析耗时100-200ms延迟图片域名与页面域名不一致考虑费用上级决策静态资源无CDN服务器带宽瓶颈baseImgUrl直接指向源站考虑费用上级决策无HTTP缓存策略重复下载依赖浏览器默认缓存不适用我们的项目我们需要实时更新首屏图片无预加载白屏时间长关键图片未优先加载首页图片可预先加载但是我们的首页是后台配置图片无法写固定值提前加载瀑布流图片无懒加载并发请求过多部分页面未使用u-lazy-load组件全部开启懒加载oss图片webp格式100-200ms延迟为增加webp后缀所有oss图片已增加webp后缀图片尺寸未适配浪费带宽未根据设备像素比裁剪已修改为读取机型适配图片可能存在的问题没有全局懒加载 大部分 u-image 组件没有启用 lazy-load 属性缺少图片尺寸控制 没有使用OSS图片处理参数如缩略图、压缩重复加载 列表滚动时可能重复加载相同图片首屏加载过多 页面初始化时加载所有可见图片缺少缓存策略 没有利用小程序图片缓存机制大图直接加载 原图直接展示没有渐进式加载- 网络层 OSS跨域请求、HTTPS握手、DNS解析- 图片体积 原图加载没有压缩或裁剪- 并发限制 小程序同时请求数量有限10个- 渲染层 大量图片同时解码导致卡顿- 缺少预加载 没有提前加载即将展示的图片当前已实现的优化使用 u-loading slot 实现加载占位u-parse组件支持 lazyLoad 懒加载图片格式化处理统一封装u-image组件默认已开启懒加载二、调研方案方案一 资源存储策略最核心痛点小程序代码包限制在 2MB过多的本地图片会挤占体积并减慢首屏解析速度。CDN加速所有的业务图片、大图、banner 必须上传至 OSS/COS 等云存储利用CDN进行分发提升加载速度。开启CDN的费用与影响是否收费CDN是独立计费的。流量费开启后产生的下行流量按 CDN 资费计算通常比 OSS 直接外网流出的流量费更便宜。回源费当 CDN 节点没缓存时会去 OSS 取数据这会产生 OSS 的回源流量费。结论对于图片较多、访问量大的小程序CDNOSS的组合通常比单纯用 OSS 更省钱因为 CDN 流量单价更低且有缓存机制。有什么影响正面影响加载速度从“秒级”提升到“毫秒级”减轻 OSS 服务器压力。负面影响缓存同步如果你替换了 OSS 上的某张图但文件名没变CDN 节点可能还缓存着旧图用户看到的还是旧的。这时需要手动在后台“刷新 URL”。预期效果开启后图片加载可缩短至 200ms 左右显著提升用户体验且 CDN 流量单价低于 OSS 外网流量可降低运营成本阿里云 CDN 计费调研报告核心计费模式按流量计费最常用CDN 主要是为了替代 OSS 直接流出流量。对比发现开启 CDN 后单价通常更低。计费项单价 (参考)说明OSS 外网流出流量约 0.50元/GB目前小程序直接访问 OSS 产生的费用CDN 下行流量约 0.24元/GB开启 CDN 后用户访问产生的费用节省幅度约 50%开启 CDN 反而能节省一半的流量费注阿里云经常有流量包促销例如 1TB/1年的流量包可能只需 100-200 元合0.1元/GB左右成本更低。回源流量费新增费用当 CDN 节点上没有缓存某张图片时它会去 OSS 下载这叫“回源”。计费约0.15元/GB。影响只有第一次访问或缓存过期时产生。一旦缓存命中之后成千上万次访问都不再产生此费用。静态资源预热可选如果图片更新非常频繁可能涉及刷新缓存的接口调用费但对于普通小程序图片固定这部分费用通常在免费额度内。调研结果方案一考虑到费用问题不做决策上述内容向上反馈由管理层做决策方案二阿里云 OSS 的域名管理里开启 HTTPS 证书并勾选HTTP/2选项1. 开启 HTTP/2效果解决图片排队等待问题。原理HTTP/1.1 下浏览器对同一个域名同时只能建立 6-8 个连接图片多了就会排队。HTTP/2 支持多路复用几十张图可以同时发送请求。操作即使没开 CDN在阿里云 OSS 的域名管理里通常也可以免费开启 HTTPS 证书并勾选HTTP/2选项。2.阿里云OSS开启 HTTP/2 具体步骤登录控制台登录 阿里云管理控制台。进入存储桶在左侧菜单栏点击Bucket 列表找到存放图片资源的那个 Bucket存储空间。进入域名管理在左侧导航栏中选择传输管理-域名管理。找到目标域名在域名列表中找到自定义域名配置证书/HTTPS点击该域名右侧的证书托管或配置。关键点必须确保“状态”为已开启HTTPS。如果没开启需要先上传证书。开启 HTTP/2在 HTTPS 配置界面中找到HTTP/2 设置选项。将开关切换为开启状态。保存生效点击确定或保存。配置通常在1-5 分钟内全网生效3.特别注意避坑指南必须是HTTPSHTTP/2 协议强制要求在加密连接HTTPS下运行。如果你们目前是 HTTP 访问开启后也不会生效 [1, 2]。浏览器兼容性现代浏览器和微信小程序底层均完美支持 HTTP/2。如果用户手机系统版本极低会自动降级回 HTTP/1.1不会影响访问 [2]。4.如何验证是否成功配置完成后回到微信开发者工具清空缓存刷新页面。在Network面板找到图片请求。查看Protocol列如果显示为h2说明已经成功开启现状调研结果该方案二基于方案1需要开启cdn才能修改为http/2方案目前未开启cdn方案无效方案三采用 “前端动态资源拦截优化” 策略1、 核心原理利用阿里云OSS免费自带的“图片处理Image Processing”功能在代码中通过“全局混入”强制将所有图片转为极小的WebP格式。阿里云 OSS 支持在 URL 后直接拼接处理指令。对于小程序我们最需要的组合是format,webp将图片转为 WebP 格式体积减小约 70%。resize,w_300按需缩放。如果是一个 100px 的头像加载 2000px 的原图就是浪费带宽。quality,q_75画质压缩。75% 是人眼几乎看不出区别、但体积缩减明显的平衡点。兼容性需要对 GIF、视频、PDF 等非图片资源做自动过滤。预期效果在不增加 CDN 费用的情况下全站图片加载速度提升 200%-300%流量成本降低 60%。降低质量 q_60 或 q_50调整尺寸 w_800 或 w_640移除压缩 image/format,webp?x-oss-processimage/resize,w_200,h_200,m_fill // 缩略图?x-oss-processimage/quality,q_80 // 质量压缩?x-oss-processimage/format,jpg // 格式转换?x-oss-processimage/interlace,1 // 渐进显示调研结果已经按照oss图片增加webp处理增加上了后缀图片有明显变化再次优化图片尺寸自适应响应式图片问题 : 当前OSS处理参数固定为 w_1080 未根据设备屏幕适配优化措施 : 根据设备像素比(DPR)动态调整图片尺寸优化前优化后代码思路直接在u-image组件接收的src直接对src进行处理以下是优化过后的u-image组件可以参考oss增加webp的逻辑template view classu-image taponClick :style[wrapStyle, backgroundStyle] image v-if!isError :srcprocessedSrc || src :modemode erroronErrorHandler loadonLoadHandler :lazy-loadlazyLoad classu-image__image :show-menu-by-longpressshowMenuByLongpress :style{ borderRadius: shape circle ? 50% : $u.addUnit(borderRadius) } /image view v-ifshowLoading loading classu-image__loading :style{ borderRadius: shape circle ? 50% : $u.addUnit(borderRadius), backgroundColor: this.bgColor } slot v-if$slots.loading nameloading / u-icon v-else :nameloadingIcon :widthwidth :heightheight/u-icon /view view v-ifshowError isError !loading classu-image__error :style{ borderRadius: shape circle ? 50% : $u.addUnit(borderRadius) } slot v-if$slots.error nameerror / u-icon v-else :nameerrorIcon :widthwidth :heightheight/u-icon /view /view /template script /** * Image 图片 * description 此组件为uni-app的image组件的加强版在继承了原有功能外还支持淡入动画、加载中、加载失败提示、圆角值和形状等。 * tutorial https://uviewui.com/components/image.html * property {String} src 图片地址 * property {String} mode 裁剪模式见官网说明 * property {String | Number} width 宽度单位任意如果为数值则为rpx单位默认100% * property {String | Number} height 高度单位任意如果为数值则为rpx单位默认 auto * property {String} shape 图片形状circle-圆形square-方形默认square * property {String | Number} border-radius 圆角值单位任意如果为数值则为rpx单位默认 0 * property {Boolean} lazy-load 是否懒加载仅微信小程序、App、百度小程序、字节跳动小程序有效默认 true * property {Boolean} show-menu-by-longpress 是否开启长按图片显示识别小程序码菜单仅微信小程序有效默认 false * property {String} loading-icon 加载中的图标或者小图片默认 photo * property {String} error-icon 加载失败的图标或者小图片默认 error-circle * property {Boolean} show-loading 是否显示加载中的图标或者自定义的slot默认 true * property {Boolean} show-error 是否显示加载错误的图标或者自定义的slot默认 true * property {Boolean} fade 是否需要淡入效果默认 true * property {String Number} width 传入图片路径时图片的宽度 * property {String Number} height 传入图片路径时图片的高度 * property {Boolean} webp 只支持网络资源只对微信小程序有效默认 false * property {String | Number} duration 搭配fade参数的过渡时间单位ms默认 500 * event {Function} click 点击图片时触发 * event {Function} error 图片加载失败时触发 * event {Function} load 图片加载成功时触发 * example u-image width100% height300rpx :srcsrc/u-image */ export default { name: u-image, props: { // 图片地址 src: { type: String, default: }, // 裁剪模式 mode: { type: String, default: aspectFill }, // 宽度单位任意 width: { type: [String, Number], default: 100% }, // 高度单位任意 height: { type: [String, Number], default: auto }, // 图片形状circle-圆形square-方形 shape: { type: String, default: square }, // 圆角单位任意 borderRadius: { type: [String, Number], default: 0 }, // 是否懒加载微信小程序、App、百度小程序、字节跳动小程序 lazyLoad: { type: Boolean, default: true }, // 开启长按图片显示识别微信小程序码菜单 showMenuByLongpress: { type: Boolean, default: true }, // 加载中的图标或者小图片 loadingIcon: { type: String, default: photo }, // 加载失败的图标或者小图片 errorIcon: { type: String, default: error-circle }, // 是否显示加载中的图标或者自定义的slot showLoading: { type: Boolean, default: true }, // 是否显示加载错误的图标或者自定义的slot showError: { type: Boolean, default: true }, // 是否需要淡入效果 fade: { type: Boolean, default: true }, // 只支持网络资源只对微信小程序有效 webp: { type: Boolean, default: false }, // 过渡时间单位ms duration: { type: [String, Number], default: 500 }, // 背景颜色用于深色页面加载图片时为了和背景色融合 bgColor: { type: String, default: #f3f4f6 } }, data() { return { // 图片是否加载错误如果是则显示错误占位图 isError: false, // 初始化组件时默认为加载中状态 loading: true, // 不透明度为了实现淡入淡出的效果 opacity: 1, // 过渡时间因为props的值无法修改故需要一个中间值 durationTime: this.duration, // 图片加载完成时去掉背景颜色因为如果是png图片就会显示灰色的背景 backgroundStyle: {}, // 处理后的图片地址添加webp后缀 processedSrc: }; }, watch: { src: { immediate: true, handler (n) { if(!n) { // 如果传入null或者或者false或者undefined标记为错误状态 this.isError true; this.loading false; this.processedSrc ; } else { this.isError false; // 处理OSS图片自动添加webp后缀 this.processedSrc this.processOssImage(n); } } } }, computed: { wrapStyle() { let style {}; // 通过调用addUnit()方法如果有单位如百分比px单位等直接返回如果是纯粹的数值则加上rpx单位 style.width this.$u.addUnit(this.width); style.height this.$u.addUnit(this.height); // 如果是配置了圆形设置50%的圆角否则按照默认的配置值 style.borderRadius this.shape circle ? 50% : this.$u.addUnit(this.borderRadius); // 如果设置圆角必须要有hidden否则可能圆角无效 style.overflow this.borderRadius 0 ? hidden : visible; if (this.fade) { style.opacity this.opacity; style.transition opacity ${Number(this.durationTime) / 1000}s ease-in-out; } return style; } }, methods: { // 点击图片 onClick() { this.$emit(click); }, // 图片加载失败 onErrorHandler(err) { this.loading false; this.isError true; this.$emit(error, err); }, // 图片加载完成标记loading结束 onLoadHandler() { this.loading false; this.isError false; this.$emit(load); // 如果不需要动画效果就不执行下方代码同时移除加载时的背景颜色 // 否则无需fade效果时png图片依然能看到下方的背景色 if (!this.fade) return this.removeBgColor(); // 原来opacity为1(不透明是为了显示占位图)改成0(透明意味着该元素显示的是背景颜色默认的灰色)再改成1是为了获得过渡效果 this.opacity 0; // 这里设置为0是为了图片展示到背景全透明这个过程时间为0延时之后延时之后重新设置为duration是为了获得背景透明(灰色) // 到图片展示的过程中的淡入效果 this.durationTime 0; // 延时50ms否则在浏览器H5过渡效果无效 setTimeout(() { this.durationTime this.duration; this.opacity 1; setTimeout(() { this.removeBgColor(); }, this.durationTime); }, 50); }, // 移除图片的背景色 removeBgColor() { // 淡入动画过渡完成后将背景设置为透明色否则png图片会看到灰色的背景 this.backgroundStyle { backgroundColor: transparent }; }, // 处理OSS图片自动添加webp格式后缀并根据尺寸自适应压缩 processOssImage(url) { if (!url || typeof url ! string) { return url; } // 判断是否为OSS图片包含oss关键词或阿里云OSS域名特征 const isOssImage url.includes(oss) || url.includes(aliyuncs.com) || url.includes(oss-cn-); if (!isOssImage) { return url; } // 如果已经包含x-oss-process参数追加format,webp if (url.includes(x-oss-process)) { // 如果已经包含webp格式不再处理 if (url.includes(format,webp)) { return url; } // 追加webp格式转换 return url /format,webp; } // 根据组件尺寸计算合适的图片宽度 const targetWidth this.getTargetWidth(); // 根据图片尺寸和使用场景选择质量参数 const quality this.getQualityBySize(targetWidth); // OSS图片处理参数自适应尺寸、转换为webp格式、动态质量 const OSS_PROCESS_PARAMS image/resize,w_${targetWidth},m_lfit/format,webp/quality,q_${quality}; // 如果没有x-oss-process参数添加完整的处理参数 const separator url.includes(?) ? : ?; return url separator x-oss-process OSS_PROCESS_PARAMS; }, // 根据组件宽度计算目标图片宽度考虑设备像素比 getTargetWidth() { try { // 获取设备信息 const systemInfo uni.getSystemInfoSync(); const dpr systemInfo.pixelRatio || 1; const screenWidth systemInfo.windowWidth || 375; // 解析组件宽度 let componentWidth this.parseWidth(this.width, screenWidth); // 根据设备像素比计算实际需要的图片宽度 let targetWidth Math.ceil(componentWidth * dpr); // 限制最大宽度避免过大图片 const MAX_WIDTH 1080; const MIN_WIDTH 100; if (targetWidth MAX_WIDTH) { targetWidth MAX_WIDTH; } else if (targetWidth MIN_WIDTH) { targetWidth MIN_WIDTH; } // 按50的倍数取整增加缓存命中率 return Math.ceil(targetWidth / 50) * 50; } catch (e) { // 异常情况下返回默认值 return 800; } }, // 解析组件宽度返回px数值 parseWidth(width, screenWidth) { if (typeof width number) { // 数值类型认为是rpx转换为px return width / 2; } if (typeof width string) { // 处理百分比 if (width.includes(%)) { const percent parseFloat(width) / 100; return screenWidth * percent; } // 处理rpx if (width.includes(rpx)) { return parseFloat(width) / 2; } // 处理px if (width.includes(px)) { return parseFloat(width); } // 纯数字字符串 if (!isNaN(parseFloat(width))) { return parseFloat(width) / 2; } } // 默认返回屏幕宽度 return screenWidth; }, // 根据图片尺寸选择质量参数 getQualityBySize(targetWidth) { // 小图标/头像高压缩率质量60 if (targetWidth 150) { return 60; } // 中等尺寸图片质量70 if (targetWidth 400) { return 70; } // 大图质量75平衡清晰度与体积 if (targetWidth 800) { return 75; } // 超大图质量80保证清晰度 return 80; } } }; /script style scoped langscss import ../../libs/css/style.components.scss; .u-image { position: relative; transition: opacity 0.5s ease-in-out; __image { width: 100%; height: 100%; } __loading, __error { position: absolute; top: 0; left: 0; width: 100%; height: 100%; include vue-flex; align-items: center; justify-content: center; background-color: $u-bg-color; color: $u-tips-color; font-size: 46rpx; } } /style方案四后端OSS资源元数据强缓存优化技术实现在后端调用阿里云 OSS SDK 上传图片的代码逻辑中通过设置ObjectMetadata对象元数据统一为资源注入以下 HTTP 响应头配置参数Cache-Control: max-age31536000实施方式在执行putObject上传操作时全局配置metadata.setCacheControl(max-age31536000)深度解析什么是max-age31536000定义Cache-Control是 HTTP 协议中控制缓存的核心指令。max-age代表资源在客户端用户手机中被视为“新鲜”的最大时间单位为秒。数值换算31536000秒 3600秒 × 24小时 × 365天 1 年。运行机制当小程序首次下载图片后手机浏览器会将该图片存入本地磁盘或内存。在未来的一年内只要图片 URL 不变手机将直接从本地读取不再向阿里云服务器发送任何网络请求。为什么需要这样做核心痛点解决消除网络排队延迟在 HTTP/1.1 协议下浏览器对同一域名的并发请求有限制。如果不设缓存每次打开页面图片都要“排队下载”。开启强缓存后图片加载跳过了网络阶段彻底解决“1-2秒才出图”的尴尬。解决重复渲染闪烁用户在切换页面或二次进入小程序时由于资源已在本地图片会随页面同步“瞬时弹出”消除先白屏、后出图的视觉闪烁感。. 方案优势与商业价值极致的加载性能0ms 响应 二次访问时图片的加载耗时将从“秒级”直接降至0毫秒显示为from memory cache或from disk cache。大幅降低运营成本省钱 阿里云 OSS 是按下行流量计费的。配置强缓存后大量重复的图片访问不再产生外网流出流量。根据行业测算此举可为公司节省30% - 60%的 OSS 流量费用。提高系统稳定性 极大降低了高并发时期 OSS 服务器的并发压力确保核心业务接口如登录、支付在高峰期拥有更多的带宽资源。风险控制如果图片需要更新怎么办文件名版本号化对于需要更新的图片如活动 Banner建议在上传时修改文件名或在前端 URL 后拼接版本号如?v20260311。结论由于 URL 变动会被视为新资源手机会自动重新下载并缓存。这确保了“静态图标永久缓存动态资源受控更新”的完美平衡。配置max-age3600(1小时)优势时效性极高。体验用户在一个小时内点击小程序是秒开的。代价相比 1 天会产生更多的 OSS 流量请求。调研结果不建议使用虽然0ms秒开图片但是因为365天意味着图片一直未更新运营侧上传的图片无法实时更新如果更新为1小时配置可以看情况考虑但是要基于我们的运营经常就该患教或者其他图片相关配置决定方案五: HTTP缓存头优化问题: 静态资源缓存策略不明确优化措施: 在OSS/服务器端配置缓存头nginx# Nginx配置示例location ~* \.(png|jpg|jpeg|gif|webp|svg)$ {expires 30d; # 图片缓存30天add_header Cache-Control public, immutable;add_header Vary Accept-Encoding;}# 带hash的文件长期缓存location ~* \.[a-f0-9]{8,}\.(png|jpg|jpeg|gif|webp)$ {expires 1y;add_header Cache-Control public, immutable;}调研结果缓存三十天的方案和上面的缓存也差不多无法实时更新方案六: 首屏关键图片预加载使用首次获取低质量压缩图获取到接口再展示清晰图问题: 首屏图片如轮播图、logo与页面同时加载造成白屏优化措施: 在App.vue中提前预加载关键图片主要处理大尺寸的图片封面图、头像等小图标20x20, 32x32, 36x36等不需要渐进式加载反而会更久首页IM头像预加载组件template view classprogressive-image-wrapper :style{ width: imgWidth, height: imgHeight, borderRadius: imgRadius } !-- 模糊缩略图 -- image v-ifshowThumb thumbUrl classprogressive-image thumb :srcthumbUrl :modemode :style{ width: imgWidth, height: imgHeight, borderRadius: imgRadius, filter: blur(10px), transform: scale(1.1) } / !-- 骨架屏 -- view v-ifloading !showThumb classprogressive-skeleton :style{ width: imgWidth, height: imgHeight, borderRadius: imgRadius, backgroundColor: bgColor } view classprogressive-shimmer/view /view !-- 高清原图 -- image classprogressive-image original :srcoriginalUrl :modemode :style{ width: imgWidth, height: imgHeight, borderRadius: imgRadius, opacity: originalLoaded ? 1 : 0, transition: opacity ${fadeDuration}ms ease-in-out } loadonOriginalLoad erroronOriginalError / /view /template script /** * ProgressiveImage 渐进式图片加载组件 * description 先加载模糊缩略图再加载高清原图实现渐进式加载效果 * property {String} src 原图URL * property {String} thumb 缩略图URL不传则自动生成 property {String | Number} width 宽度默认100% * property {String | Number} height 高度默认200rpx * property {String | Number} borderRadius 圆角默认0 * property {String} mode 图片裁剪模式默认aspectFill * property {String} bgColor 骨架屏背景色默认#f0f0f0 * property {Number} fadeDuration 淡入动画时长单位ms默认300 * property {Number} thumbQuality 缩略图质量 1-100默认30 * property {Number} thumbWidth 缩略图宽度默认200 * example * progressive-image * srchttps://example.com/image.jpg * width300rpx * height200rpx * borderRadius12rpx * /progressive-image */ export default { name: ProgressiveImage, props: { src: { type: String, default: }, thumb: { type: String, default: }, width: { type: [String, Number], default: 100% }, height: { type: [String, Number], default: 200rpx }, borderRadius: { type: [String, Number], default: 0 }, mode: { type: String, default: aspectFill }, bgColor: { type: String, default: #f0f0f0 }, fadeDuration: { type: Number, default: 300 }, thumbQuality: { type: Number, default: 30 }, thumbWidth: { type: Number, default: 200 } }, data() { return { loading: true, originalLoaded: false, thumbLoaded: false, loadError: false } }, computed: { imgWidth() { return this.$u.addUnit(this.width); }, imgHeight() { return this.$u.addUnit(this.height); }, imgRadius() { if (this.borderRadius circle || this.borderRadius 50%) { return 50%; } return this.$u.addUnit(this.borderRadius); }, // 生成缩略图URL thumbUrl() { if (this.thumb) return this.thumb; if (!this.src) return ; // 如果是OSS图片添加压缩参数 if (this.src.includes(aliyuncs.com) || this.src.includes(oss-)) { const params [ resize,w_${this.thumbWidth}, format,webp, quality,q_${this.thumbQuality} ]; return ${this.src}?x-oss-processimage/${params.join(/)}; } return this.src; }, originalUrl() { return this.src; }, showThumb() { return this.thumbUrl !this.originalLoaded !this.loadError; } }, watch: { src: { immediate: true, handler(newVal) { if (newVal) { this.loading true; this.originalLoaded false; this.loadError false; this.preloadThumb(); } } } }, methods: { // 预加载缩略图 preloadThumb() { if (!this.thumbUrl) return; uni.downloadFile({ url: this.thumbUrl, success: () { this.thumbLoaded true; }, fail: () { // 缩略图加载失败直接显示骨架屏等待原图 this.thumbLoaded false; } }); }, // 原图加载完成 onOriginalLoad() { this.originalLoaded true; this.loading false; this.$emit(load); }, // 原图加载失败 onOriginalError() { this.loadError true; this.loading false; this.$emit(error); } } } /script style scoped langscss .progressive-image-wrapper { position: relative; overflow: hidden; background-color: #f5f5f5; } .progressive-image { position: absolute; top: 0; left: 0; will-change: opacity, transform; .thumb { z-index: 1; } .original { z-index: 2; } } .progressive-skeleton { position: absolute; top: 0; left: 0; z-index: 0; overflow: hidden; } .progressive-shimmer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient( 90deg, transparent 0%, rgba(255, 255, 255, 0.4) 50%, transparent 100% ); animation: shimmer 1.5s infinite; } keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } /style方案7: 骨架屏优化已部分实现现状: 已有Skeleton组件但未全面应用优化建议: 在图片加载区域使用骨架屏占位太快截图不到闪烁骨架屏组件template !-- 图片骨架屏占位组件 -- view classimage-skeleton-wrapper :style{ width: skeletonWidth, height: skeletonHeight, borderRadius: skeletonRadius } view v-ifloading classimage-skeleton :style{ width: 100%, height: 100%, borderRadius: skeletonRadius, backgroundColor: bgColor } view classimage-skeleton__shimmer/view /view slot v-else/slot /view /template script /** * ImageSkeleton 图片骨架屏占位组件 * description 在图片加载前显示骨架屏占位提升用户体验 * property {String | Number} width 宽度支持rpx、px、%默认100% * property {String | Number} height 高度支持rpx、px默认200rpx * property {String | Number} borderRadius 圆角默认0 * property {Boolean} loading 是否显示骨架屏默认true * property {String} bgColor 骨架屏背景色默认#f0f0f0 * example * image-skeleton width200rpx height200rpx borderRadius50% :loading!imageLoaded * u-image width200 height200 :srcimageUrl loadimageLoaded true/u-image * /image-skeleton */ export default { name: ImageSkeleton, props: { width: { type: [String, Number], default: 100% }, height: { type: [String, Number], default: 200rpx }, borderRadius: { type: [String, Number], default: 0 }, loading: { type: Boolean, default: true }, bgColor: { type: String, default: #f0f0f0 } }, computed: { skeletonWidth() { return this.$u.addUnit(this.width); }, skeletonHeight() { return this.$u.addUnit(this.height); }, skeletonRadius() { if (this.borderRadius circle || this.borderRadius 50%) { return 50%; } return this.$u.addUnit(this.borderRadius); } } } /script style scoped langscss .image-skeleton-wrapper { position: relative; overflow: hidden; } .image-skeleton { position: relative; overflow: hidden; __shimmer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient( 90deg, transparent 0%, rgba(255, 255, 255, 0.4) 50%, transparent 100% ); animation: shimmer 1.5s infinite; } } keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } /style方案5: 静态资源本地缓存策略问题: 静态图标每次都需要网络请求优化措施: 将常用小图标转为Base64或使用小程序本地资源思考我们的静态资源都是放在服务器上通过服务器路径拼接如果常用小图标改为本地资源是否会影响小程序包体积大小

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2411622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…