尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

uni-app x实战:鸿蒙跨平台开发网络封装与轮播图黑边修复

uni-app x实战:鸿蒙跨平台开发网络封装与轮播图黑边修复 2024 年之后还在做移动端开发的人几乎绕不开一个问题鸿蒙到底要不要投入我的答案是先空出时间认真试一遍因为跨平台框架已经把试错成本压到很低的程度。uni-app x 就是其中一个让我愿意花时间研究的方向它和传统 uni-app 最大的不同在于它不再依赖 WebView 套壳而是直接把代码编译成各端原生产物也包括 HarmonyOS 的原生应用。这篇文章我准备用一次实际项目里的两个核心模块把跨平台开发中最容易卡住人的地方讲透网络请求模块封装和首页轮播图实现。网络封装解决的是多端请求的统一问题轮播图则是内容型 App 里最高频的组件之一。为了不让文章停留在“能跑”我会把在 Android 和鸿蒙真机上遇到的问题尤其是轮播图黑边这种又隐蔽又影响体验的细节尽量完整地还原出来给正在做同样技术选型的朋友一个参考。1. 认清 uni-app x它不是旧的 uni-app 换个名字1.1 多端并行下的技术选型逻辑移动端开发这几年的复杂度是持续上升的。早期只需要盯 iOS 和 Android后来各家小程序越来越多现在鸿蒙又成了必须考虑的端。一个产品想全平台覆盖要么养多套原生团队要么接受一套 H5 套壳方案在体验上打折扣。React Native 和 Flutter 都是成熟的跨端框架但前者在鸿蒙上的支持主要靠社区适配后者需要团队掌握 Dart并且和现有前端技术栈有一定隔阂。uni-app x 走的是另一条路保留 Vue 的开发体验用类似 TypeScript 的 uts 语言编写逻辑再编译输出成各端原生代码。对于已经熟悉 Vue 和小程序开发模式的人来说迁移成本很低这也是它在国内团队里接受度比较高的核心原因。真正让我决定用 uni-app x 做鸿蒙相关项目的点是它对 HarmonyOS 的原生支持。传统 uni-app 在鸿蒙上其实还是通过 WebView 渲染性能和原生能力调用都有明显瓶颈。uni-app x 则直接针对鸿蒙生成 ArkTS 代码和原生组件最终构建出 HAP 包。这意味着你在页面上写的一个swiper在鸿蒙设备上就是真正的原生轮播组件而不是网页模拟出来的效果你调用的网络请求也会走鸿蒙系统底层的网络能力而不是浏览器 XMLHttpRequest。跨端方案做到这个程度才真正具备“一套代码、多处原生运行”的价值。1.2 uni-app x 在鸿蒙平台上的编译机制很多人第一次接触 uni-app x 时会误以为它只是 uni-app 的改版实际差异比想象中大。uni-app x 的产物不再经过 JavaScript 引擎解释运行而是被编译成各端原生语言。以鸿蒙为例核心逻辑会转换为 ArkTSUI 结构会映射到鸿蒙原生组件树再通过鸿蒙的构建工具链打包。正因为如此它的运行性能、内存占用和原生应用几乎一致同时也继承了各端原生组件在不同系统上的真实表现。这件事有好的一面也有需要警惕的一面。好的一面是性能有保证复杂交互不会像 WebView 方案那样出现明显掉帧。需要警惕的地方在于既然走了原生组件各个平台对组件属性的实现就不可能 100% 一致。比如 Android 上正常的样式到了鸿蒙上可能会因为组件底层渲染差异出现奇怪的表现轮播图黑边问题就是这么来的。这种情况不是框架学得不好而是你对底层渲染机制的理解还停留在 Web 时代。所以在实战项目里多花一点时间了解组件在不同端的渲染差异比单纯背 API 有用得多。1.3 项目整体结构与模块划分在实际开始写代码之前我先把项目结构定下来。好的结构能决定你后面加功能、修 bug 的效率尤其是跨端项目不确定因素多分层清楚能省下大量排查时间。下面是这次实战项目的目录骨架├── pages/ │ ├── index/ │ │ └── index.uvue │ ├── login/ │ │ └── login.uvue │ └── ... ├── components/ │ ├── banner/ │ │ ├── banner.uvue │ │ └── banner.scss │ └── ... ├── utils/ │ └── request.ts ├── api/ │ └── home.ts ├── static/ │ ├── images/ │ └── logo.png └── manifest.json几个关键设计的考虑第一网络请求层单独放utils/request.ts页面和组件都不允许直接写uni.request统一走封装的get和post方法。这样后续如果把接口从 HTTP 切到 HTTPS或者要在请求头里统一加版本号只需要改一个文件。第二接口定义放在api/目录里和页面逻辑解耦。首页需要拉轮播图数据api/home.ts里定义getBannerList()方法页面只管调用不用关心 URL 怎么拼接。第三轮播图封装成独立组件components/banner/banner.uvue通过 props 接收外部传入的数据源。这样组件不关心数据从哪来静态写死也好接口请求也好甚至后续从本地缓存读取也好都能复用同一个组件。第四页面组件用.uvue后缀脚本语言用uts。这个后缀和传统.vue的区别是 uni-app x 的编译器和编辑器的语法提示会针对原生环境做处理很多 CSS 特性在原生环境并不支持写代码时要注意避开比如filter、backdrop-filter这类样式在部分端无效。2. 网络模块封装一层经得起多端考验的请求底座2.1 直接从 uni.request 起步的问题网络请求是所有 App 的命脉但我在很多项目里看到的写法都是直接在页面里散落一堆uni.request。这样做在只有一个页面、两个接口的时候没什么问题等接口数量上到几十个痛点就非常明显。第一个痛点是 BaseURL 重复。每个请求都要写一遍https://api.xxx.com一旦后端切换域名全局搜索替换能改到怀疑人生。第二个痛点是错误处理不统一。有的页面请求失败后弹出“网络错误”有的页面直接没反应用户根本不知道发生了什么。第三个痛点是 token 注入逻辑分散。登录后拿到 token每个请求都需要手动带上Authorization头漏写一个接口就要排查半天。第四个痛点是 loading 状态管理混乱。有的请求需要全屏 loading有的不需要如果每个页面各自 showLoading再各自 hideLoading遇到并发请求时 loading 提前关闭体验很差。这些问题在单端开发里已经够烦了到鸿蒙和 Android 多端并行后还会被放大。比如 Android 上请求正常鸿蒙上因为证书或者明文流量限制直接请求失败如果你每个页面都是裸写uni.request排查范围会非常大。所以封装网络层不是锦上添花而是跨端项目能不能顺利进行下去的基础工程。2.2 网络模块封装思路与核心代码实现网络模块的封装思路可以归纳为一句话把变化的部分交给调用方把不变的部分留在底座。变化的部分包括 URL、请求方式、参数、是否需要 loading不变的部分包括 BaseURL、超时时间、请求头注入、统一的成功和失败处理、业务状态码判断。基于这个思路我会在utils/request.ts里写这样一个封装// utils/request.ts const BASE_URL: string https://api.example.com interface RequestOptions { url: string method?: GET | POST | PUT | DELETE data?: any header?: Recordstring, string showLoading?: boolean loadingText?: string timeout?: number } interface ApiResponseT { code: number message: string data: T } export function requestT(options: RequestOptions): PromiseT { return new PromiseT((resolve, reject) { if (options.showLoading) { uni.showLoading({ title: options.loadingText || 加载中..., mask: true }) } const token: string uni.getStorageSync(token) || uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, timeout: options.timeout || 10000, header: { Content-Type: application/json, Authorization: token, App-Version: 1.0.0, ...options.header }, success: (res) { const status res.statusCode if (status 200 status 300) { const body res.data as ApiResponseT if (body.code 0) { resolve(body.data) } else { uni.showToast({ title: body.message || 请求失败, icon: none }) reject(new Error(body.message)) } } else { handleHttpError(status) reject(new Error(HTTP ${status})) } }, fail: (err) { uni.showToast({ title: 网络异常请稍后重试, icon: none }) reject(err) }, complete: () { if (options.showLoading) { uni.hideLoading() } } }) }) } function handleHttpError(status: number) { if (status 401) { uni.removeStorageSync(token) // 实际项目里这里可以配合事件总线通知所有页面跳登录 uni.reLaunch({ url: /pages/login/index }) } } export function getT(url: string, data?: any): PromiseT { return requestT({ url, method: GET, data }) } export function postT(url: string, data?: any): PromiseT { return requestT({ url, method: POST, data }) }这段代码有几个关键细节值得展开说。method类型被限定为GET | POST | PUT | DELETE这四个字面量这样在调用request时如果传了不支持的请求方式编译阶段就能发现错误。get和post是面向业务方的简化方法页面里不用关心完整的 options 结构直接getBannerList () getBannerItem[](/banner)就可以了。token的获取放在请求发出前从本地存储里读出来后注入 header。这里有一个容易忽略的点uni.getStorageSync在没有数据时返回的是空字符串所以const token: string uni.getStorageSync(token) || 能保证类型安全同时避免 header 里出现Authorization: null这种不合法值。超时时间默认 10 秒。这个值需要根据业务场景调整简单的新闻列表 10 秒足够但上传图片这种请求就应该单独设置更长的timeout。封装的好处在这里体现出来业务方可以根据请求类型传不同的超时值而不影响默认逻辑。2.3 鸿蒙适配明文限制、超时与请求头鸿蒙端和 Android 端在网络上最大的差异不是 API 不同而是底层网络栈的行为不同。很多在 Android 上正常的接口放到鸿蒙真机上会直接请求失败最常见的三个原因是明文流量限制、超时机制差异、请求头校验。明文流量限制是 Android 和鸿蒙都有的机制。Android 9 以上默认禁止 HTTP 明文请求鸿蒙从 API 版本提升后同样默认禁止。如果你在开发阶段用的是http://192.168.x.x这种局域网接口需要在工程配置里明确允许明文流量否则请求会在系统网络层被直接拦截你根本到不了fail回调的阶段看起来就像网络完全挂掉。我建议开发阶段就统一使用 HTTPS如果后端实在没配证书才考虑临时放开明文限制上线前务必改回。超时机制方面鸿蒙的原生网络栈对超时参数的处理和 Android 有些不同有些版本下timeout设置过短会导致请求在弱网络环境下频繁失败。我给的建议是默认超时不要低于 10 秒上传类请求单独配置 30 秒以上这样能减少很多低概率的偶发失败。请求头方面部分后端会校验 User-Agent 或者期望某些自定义头。uni-app x 在鸿蒙上发出的 User-Agent 和浏览器、Android 都不一样如果你的后端有反爬或者风控逻辑很可能把鸿蒙端的请求误伤。解决方法是和后端确认白名单策略或者在后端允许的 User-Agent 列表里加上鸿蒙的标识。实际排查中我还遇到过后端对Content-Type大小写敏感的问题所以封装里统一用标准的Content-Type: application/json并且不手动修改它。2.4 统一处理 Loading、Token 与业务错误上面的 request 封装已经包含了基础的 Loading 和错误处理但实际项目里还会遇到一个更隐蔽的问题并发请求时Loading 会被提前关闭。比如一个页面同时发起接口 A 和接口 BA 先返回在complete回调里执行了uni.hideLoading()这时 B 还没返回但 Loading 已经消失。如果是分页加载还好如果是依赖遮罩层防止用户重复操作的场景这是明显的体验 bug。解决办法是给 Loading 加一个计数器。只有计数器归零时才真正关闭 Loadinglet loadingCount: number 0 function showLoadingIfNeeded(show: boolean, text: string) { if (show) { loadingCount if (loadingCount 1) { uni.showLoading({ title: text || 加载中..., mask: true }) } } } function hideLoadingIfNeeded(show: boolean) { if (show) { loadingCount-- if (loadingCount 0) { uni.hideLoading() loadingCount 0 } } }把这段逻辑嵌入到 request 的对应位置把原来的uni.showLoading和uni.hideLoading替换成这两个方法即可。注意loadingCount在请求失败时也要递减否则计数器越积越多Loading 永远不会关闭。Token 的处理同样有细节。很多项目只在请求头里注入 token却漏了处理 401 过期。上面的handleHttpError里已经写了收到 401 时先清除本地 token再跳转登录页。这里我还要补充一个经验如果 token 过期时当前页面有一堆弹窗或未保存的数据直接reLaunch跳登录会丢失用户当前上下文。更稳妥的做法是抛出一个自定义事件让一个全局监听者来决定如何跳转。不过在小项目里直接跳登录页是性价比最高的方案。业务错误码的处理也需要统一。很多后端的成功返回码并不是 HTTP 200而是code: 0HTTP 200 只是传输层成功。所以我在代码里先判断 HTTP 状态码再判断业务码body.code。业务码非 0 时统一用uni.showToast展示 message这样后端返回“商品已下架”“库存不足”这类信息时前端不用在每个页面重复写 toast 逻辑。3. 轮播图组件实战从能跑到不踩黑边3.1 用 swiper 搭出基础轮播图网络层有了接下来就到页面里最显眼的轮播图。uni-app x 提供了原生swiper组件沿用 Vue 语法和传统 uni-app 的用法非常接近。基础结构如下template view classbanner-wrapper swiper classbanner-swiper :autoplaytrue :circulartrue :interval4000 :duration500 :currentcurrentIndex changeonSwiperChange swiper-item v-for(item, index) in bannerList :keyindex image classbanner-image :srcitem.cover modeaspectFill clickonBannerClick(item) / /swiper-item /swiper /view /template script languts setup type BannerItem { id: number cover: string linkUrl: string title: string } const props defineProps{ bannerList: BannerItem[] }() const currentIndex ref(0) function onSwiperChange(e: any) { currentIndex.value e.detail.current } function onBannerClick(item: BannerItem) { if (item.linkUrl) { uni.navigateTo({ url: item.linkUrl }) } } /script几个属性的作用要说清楚。autoplay控制自动播放设为 true 后每interval毫秒切换一次circular控制是否循环播放如果只传autoplay不传circular轮播图播到最后一张就会停下来看起来像卡住了current是当前显示项的索引受控和非受控都想支持所以用currentIndex绑定并在change事件里更新。这里有一个常见误区很多人在swiper-item上直接写height: 100%结果图片高度撑开不了。原因是在原生端swiper的高度依赖外部容器或自身直接指定swiper-item的高度继承并不可靠。最稳妥的方案是给swiper本身一个明确的高度比如设计稿 750px 宽、360px 高就在 CSS 里写成height: 360rpx。如果你希望轮播图高度跟随图片实际比例需要动态计算这一点在后面的黑边小节里细说。3.2 安卓/鸿蒙轮播图黑边的根因与修复现在进入这篇文章最重点的部分轮播图黑边问题。热词里专门有一条“uniapp轮播图安卓有黑边”说明这不是个例而是大量开发者都会遇到的痛点。我在鸿蒙和 Android 真机上排查这个问题的过程中总结出了三个高频根因。第一个高频根因是图片的mode属性设置不当导致留白区域露出背景色。image组件的modeaspectFit会在不裁剪图片的前提下完整展示图片但图片和容器的宽高比不一致时图片两侧或上下就会出现空白区间。如果swiper的背景色是黑色这块空白就是黑边。解决方法是换用modeaspectFill它会等比放大图片直至铺满整个容器再裁剪掉超出部分。虽然会牺牲一点点图片边缘但内容型 App 的轮播图基本都是这种处理方式。第二个高频根因是图片加载完成前容器高度为 0 或者背景透出黑色。轮播图数据从接口返回后图片 src 才开始加载网络慢时组件先渲染出一个空白区域此时背景是默认黑色用户就会看到一条黑边或者黑色闪烁。解决的思路有三个方向第一给swiper和swiper-item设置和页面背景一致的背景色最常用的是白色第二在图片加载完成前用一个占位背景色填充第三尽量压缩图片资源首图用 CDN 加速。第三个高频根因是圆角裁剪引起的黑边。很多设计稿里的轮播图带有圆角开发者习惯直接给image加border-radius但图片本身没有完全适配圆角四角区域就会露出黑色的容器底色形成类似黑边的效果。这个问题的稳妥解法是不要直接给image加圆角而是给外层的view设置圆角和overflow: hidden让图片作为内层元素被裁剪view classbanner-radius image classbanner-image :srcitem.cover modeaspectFill / /view.banner-radius { width: 100%; height: 360rpx; border-radius: 24rpx; overflow: hidden; } .banner-image { width: 100%; height: 100%; }为了更直观我把黑边问题和对应的排查优先级整理成一个表格现象大概率根因推荐解法图片两侧或上下有黑条mode 设置成 aspectFit改成 aspectFill图片加载期间闪黑容器背景色是黑色swiper 和 item 设置白色背景图片四角有黑边圆角直接加在 image 上外层 view 设置圆角并 overflow: hidden轮播图高度不稳时高时低swiper 高度写死但图片比例不固定动态计算高度或统一图片输出比例透明 PNG 边缘有黑色锯齿图片本身带 alpha 通道换 JPG 或重新导出图片如果你是第一次遇到黑边我的建议是按照表格从上往下排查。先把 mode 改成aspectFill再把容器背景色统一成白色这两个步骤能解决 80% 的问题。如果还有黑边再检查圆角和图片资源。不要一上来就怀疑框架的 bug绝大多数黑边问题都出在资源或样式上。3.3 动态高度让轮播图真正配合图片比例除了上面三种常见情况还有一种黑边是“技术层面正确但视觉上很丑”那就是swiper高度写死但每张图片的比例不一样。比如第一张是 16:9第二张是 4:3固定高度下第二张图片会被大幅裁剪裁过头看起来就像图片的一部分消失。这种情况下比较优雅的方案是动态计算 swiper 高度。思路是监听图片的加载事件拿到图片的真实宽高比再根据容器宽度换算出高度。下面是一个简化后的实现const swiperHeight ref(360) function onImageLoad(e: any) { const width: number e.detail.width const height: number e.detail.height if (width height) { // 假设容器宽度是 750rpx用宽高比计算实际高度 swiperHeight.value Math.round((height / width) * 750) } }然后在模板里把高度绑定给swiperswiper-item内部的图片再把mode设置为aspectFill。这样轮播图会跟随第一张加载完成的图片尺寸调整高度后续图片如果比例不同也能通过裁剪保持视觉稳定。需要注意动态高度方案只会以某一张图片的比例为准如果所有图片比例都不一致切换时高度仍然会跳动。所以在真实项目中我依然强烈建议对轮播图素材统一输出比例比如设计稿固定 750x360前端固定这个比例后端压缩图的时候也按这个比例裁剪。数据规范比代码技巧更能根治问题。3.4 自定义指示器避免默认样式的割裂感系统自带的indicator-dots在不同平台上的默认样式不完全统一Android 上是圆形小点鸿蒙上可能颜色和间距都有差异。如果你对 UI 一致性有要求关掉系统指示器自己画一组点代码很简单view classbanner-dots view v-for(item, index) in bannerList :keyindex :class[ banner-dot, currentIndex index ? banner-dot-active : ] /view /view.banner-dots { position: absolute; right: 0; left: 0; bottom: 24rpx; display: flex; justify-content: center; } .banner-dot { width: 10rpx; height: 10rpx; margin: 0 8rpx; background-color: rgba(255, 255, 255, 0.4); border-radius: 50%; transition: all 0.3s ease; } .banner-dot-active { width: 28rpx; background-color: #ffffff; border-radius: 6rpx; }这里我用了transition让指示器的宽度变化有动画效果页面在 Android 和鸿蒙上的表现都良好。需要提醒的是swiper的change事件在快速滑动时可能会连续触发指示器更新逻辑里最好加上一层判断比如当前索引与目标索引差距过大时才更新避免 UI 闪烁。不过常规配置下直接用current绑定就能满足需求。4. 真机调试与问题排查实录4.1 用 HBuilderX 把项目跑到鸿蒙真机写代码归写代码跨端项目真正的问题往往在真机上才能暴露。uni-app x 跑鸿蒙真机的流程和 Android 有点相似但有一些前置条件容易卡住第一次操作的人。第一步是准备环境。HBuilderX 要更新到支持鸿蒙的版本建议直接下载最新正式版老版本可能连“运行到鸿蒙”的入口都没有。另外需要安装 DevEco Studio它不只是编辑器还负责提供鸿蒙 SDK 和 hdc 命令行工具HBuilderX 底层构建时会调用到这些能力。第二步是连接设备。鸿蒙手机开启开发者模式和 USB 调试后用数据线连接电脑。在终端执行hdc list targets如果能看到设备序列号说明连接成功。如果看不到设备优先检查数据线是不是只支持充电、手机有没有弹窗要求授权 USB 调试以及 hdc 的环境变量是否配置正确。第三步是在 HBuilderX 里运行。打开项目后菜单选择“运行 - 运行到手机或模拟器 - HarmonyOS”。首次运行会让选择鸿蒙 SDK 路径指定给 DevEco Studio 的 SDK 所在目录即可。构建完成后会自动安装到手机如果没有配置签名HBuilderX 会使用测试签名这没问题只在真机调试阶段使用。发布到应用市场时才需要换成正式签名。如果安装时报“签名不一致”错误通常是因为手机上已经有一个不同签名的同包名应用先卸载旧应用再安装。如果日志一直不输出检查手机端是否开启了日志捕获权限有些鸿蒙版本默认不输出 release 模式日志。4.2 网络请求失败快速定位清单网络请求失败是排查成本最高的一个问题因为涉及前端、后端、设备、网络环境多个环节。我总结了一张快速定位清单按顺序执行能省下不少时间。嫌疑点判断方法解决办法接口地址不可达用电脑 curl 接口地址看是否能返回数据确认地址、端口、防火墙明文流量被拦截请求走到 fail 回调但后端日志里没有请求记录开发期临时放开明文流量上线换 HTTPS证书问题错误信息里提示 certificate使用正规证书或开发期配置忽略校验网络环境手机和电脑是否在同一局域网真机访问电脑局域网 IP不要用 localhost请求头问题后端日志能看到请求但返回 4xx检查 User-Agent、Content-Type、Authorization业务 code 非 0HTTP 200 但业务异常看后端返回的 message大概率是业务逻辑问题有一条经验特别值得分享当请求失败时先在封装的fail回调里打印完整的错误对象再用电脑 curl 同样的接口放一起对比。这样能快速区分问题出在前端还是后端。如果你发现电脑 curl 能通、真机上通不了那 90% 是网络环境或系统权限问题如果 curl 也不通那就得找后端一起查了。4.3 轮播图在鸿蒙端的渲染异常处理鸿蒙端的原生渲染和 Android 不完全一样我在实际项目中遇到过两个比较典型的轮播图问题列举出来供参考。第一个是轮播图偶尔出现整块白屏。排查下来是指纹图片加载超时或解码失败组件渲染了一个空白区域。处理方式是为image增加error事件在图片加载失败时替换成一张本地兜底图同时把图片资源从大图改为 CDN 压缩图显著降低了失败概率。第二个问题是快速滑动时指示器状态闪烁。原因是current在滑动过程中连续变化而指示器样式切换逻辑里存在性能开销。改善的做法是在change事件里加一个防抖或者改用animationfinish事件来更新指示器这个事件只在动画结束后触发一次更适合当前类型的 UI 同步场景。第三个问题跟鸿蒙的屏幕适配有关。某些折叠屏或平板设备上rpx适配的比例和手机端不同轮播图高度有偏差。针对这个情况建议对平板设备单独做媒体查询或者用百分比高度而不是完全依赖固定 rpx 值。跨端项目里“一屏通用”的想法越早放弃越好。5. 一些后话项目扩展建议与个人心得文章写到这核心内容基本都覆盖了。最后我再分享几个实际项目里推进 uni-app x 的经验以及后续可以继续深挖的方向。如果你做的项目是内容型产品轮播图只是第一个需要封装的组件后面大概率还有消息流列表、瀑布流、视频播放器。这些组件都建议遵循同样的思路独立封装、props 传数据、不直接依赖接口层。这样组件库越来越丰满后新页面基本就是“搭积木”效率和稳定性都会有质的提升。网络模块方面目前封装的是完整可用的基础版但还可以加一层缓存策略。比如一些不常更新的接口可以先从本地缓存读数据渲染再在后台静默刷新用户感知会好很多。图片资源方面建议开启 CDN 的 WebP 和 AVIF 格式输出配合modeaspectFill能进一步减小体积而且能缓解鸿蒙和 Android 上大图加载的黑边闪烁问题。还有一点是持续跟进 uni-app x 的版本更新。跨端框架迭代节奏快鸿蒙的 SDK 也在不断升级每隔一段时间 HBuilderX 就会修复一批平台差异问题。我通常会在每次升级后先把现有的核心页面在两端真机上跑一遍专门看网络请求和轮播图这种高频模块有没有回归问题确保框架升级不带来隐藏 bug。最后再回到黑边这个问题。复盘下来轮播图黑边八成不是框架的锅而是图片资源和容器样式没有配合好。我现在的项目管理规范里会明确写所有轮播图图片统一输出为 JPG、固定宽高比、加载前留白区域使用页面背景色。把这个规范沉淀下来比临时解决一次黑边更有价值。希望这篇实战记录能帮你在 uni-app x 和鸿蒙这条路上少踩几个坑把时间花在真正有意思的需求上。
返回列表