资讯详情

鸿蒙PC应用开发实战:用ArkTS与ArkUI构建信息展示应用

📅 2026/10/20 19:02:46 | 华诺云谱 👁 阅读
鸿蒙PC应用开发实战:用ArkTS与ArkUI构建信息展示应用
做鸿蒙 PC 应用有一个很有意思的现象很多人觉得 PC 版就是把手机版放大一下结果一适配就翻车。桌面窗口的尺寸、信息密度、交互方式和手机完全不是一个逻辑。“冀游助手”这个项目就是我想在鸿蒙 PC 上验证信息类应用到底该怎么组织页面才不别扭。我的最终产物是一个没有任何后端依赖的河北简介应用概括起来就是三件事首页按分类展示河北的景点、美食、民俗手工艺点进详情能看图文长文看中的条目可以收藏。全部代码都在本地用 ArkTS 编写UI 用 ArkUI 声明式组件搭建数据由 JSON 驱动。这篇东西适合谁看如果你是刚接触鸿蒙应用开发又想顺手做点能放进作品集的小项目这篇文章可以当成一条从环境搭建到打包运行的完整路线图。我会把搭建过程中踩过的坑、调过的参数一并写出来尽量让你照着能复现。项目本身难度不高但信息展示类应用恰恰是开发中最常见、最能锻炼基本功的场景把这里面的逻辑吃透了后面做任何内容型应用都能少走弯路。1. 项目从哪来做一款“冀游助手”的缘起与整体思路1.1 为什么选择鸿蒙PC版当载体我在决定做这个项目之前先盘了一下手里的技术路线手机端应用我已经写过几个平板端也试过但 PC 窗口一直没正经做过。之所以这次选鸿蒙 PC 版首先是盯上了它的“一多”能力也就是一套代码能跑在不同形态的设备上。PC 窗口比手机大得多信息展示类应用的体验天然更舒服。旅游简介这种内容在手机上看图、看文、收藏来回切换非常割裂但放到 PC 屏幕上左边是景点卡片列表右边是详情内容鼠标一点就能切换这个效率完全是另一档。其次是交互方式的差异。PC 端有鼠标和完整的键盘事件可以支持悬浮预览、右键快捷收藏、CtrlF 之类的操作。我在冀游助手里就做了一个很简单的交互鼠标放到卡片上时卡片会有轻微放大和阴影加深的效果这在手机上是很难自然呈现的。HarmonyOS 对窗口交互事件的支持已经比较完整使用 ArkUI 的鼠标事件和状态样式就能实现不需要额外引入复杂的库。再说市场定位。现在 PC 应用商店里真正用心做本地文化展示的应用并不多大多数城市类应用都把重点放在“能导航、能订票”这样的工具性功能上反而不太关注阅读体验。冀游助手不做太重的功能它的定位就是“深入阅读一个地方”这种小而美的内容应用放到 PC 大屏上反而更有优势。用 PC 的时候用户注意力相对集中看完一篇文章、浏览一套资料是常见行为。1.2 冀游助手的产品边界不做大而全做小而美产品范围我刻意收得很窄。一开始有人建议我把河北省所有地级市都做进去每个城市再挂上酒店、交通、购物这些信息我果断拒绝了。一个练习项目如果把范围铺开大概率会出现两种结局要么收尾收不干净要么每一块都做得特别浅。最终我定了三个模块景点、美食、手工艺。景点我拿了几个有代表性的场景美食选了大家比较熟悉的品类再加两个小众的手工艺则完全做成文化介绍性质。这种取舍背后有三个考虑。第一数据量小意味着我可以把所有精力放在界面交互和状态管理上毕竟这个项目的核心目标是实践 ArkUI 组件和调试流程。第二信息展示类应用的卡点从来不是“内容够不够多”而是“内容多的时候列表性能能不能扛住”所以我在项目里特意做了列表渲染方式的对比。第三内容范围收窄以后详情页可以写得比较深每篇文章控制在 300 到 500 字配图也能保持统一风格整体完成度会高很多。从工程角度看这个产品边界也给后续扩展留了路。数据文件是 JSON 格式页面展示完全由数据驱动以后想加城市模块只需要在 JSON 里新增条目对应的详情页路由会自动处理好。所以冀游助手虽然现在只是展示局部内容架构上已经是可以横向扩充的。2. 开发前的准备环境搭得顺后面少遭罪2.1 环境与SDK先跑通Hello World再谈UI鸿蒙开发环境的搭建比较直接目前官方主推的是 DevEco Studio 这套 IDE。第一次启动的时候按向导把 HarmonyOS SDK 配置好打开设备管理创建一个本地模拟器。这里我有一个非常实际的建议不要急着开始写业务代码先把工具链跑通。我见过太多人一上来就照着教程写 UI结果一运行才发现在模拟器创建环节就卡住了后面全部代码都得推倒重来。创建项目的时候我选择的是 Empty Ability 模板语言直接选 ArkTS。这个模板会生成一个最简单的可运行应用包含一个文本页面和一个默认的入口能力。先用这个模板在模拟器上跑一次 Hello World确认三个关键链路是通的编译没问题、模拟器能启动、应用能安装。这两个环节任何一个出问题后面都很难排查因为你会分不清问题是出在环境上还是出在你的代码上。DevEco Studio 自带的预览器 Previewer 也很好用它可以在不启动模拟器的情况下快速渲染单个页面。我在写首页卡片布局的时候就是一边改代码一边看预览器的实时效果。但预览器有几个局限它不能完全模拟 PC 窗口的真实尺寸也不能验证路由跳转和系统能力调用。所以我的习惯是写页面时用预览器快速调样式做流程验证和最终测试时用模拟器。2.2 工程结构设计一个页面搞定还是拆文件很多初学者会觉得项目就那么几个页面全部塞进 Index.ets 里不就行了这个想法在体验阶段没问题但只要页面一多维护成本立刻上来。我给冀游助手设计的工程结构是这样entry/src/main/ets/ ├── entryability/ │ └── EntryAbility.ets ├── pages/ │ ├── Index.ets │ └── Detail.ets ├── component/ │ ├── SpotCard.ets │ └── SectionTitle.ets ├── model/ │ └── Spot.ets ├── data/ │ └── hebei_data.json └── common/ └── Constants.etspages 目录只放路由页面component 放公用的 UI 子组件model 定义数据模型data 放 JSON 数据文件。这样拆有什么好处最直接的一点是当我要改卡片样式的时候只需要打开 SpotCard.ets完全不用动页面逻辑当我要增加一个模块分类时也只需要在 JSON 里加内容再决定要不要新增一个页面。还有一个容易被忽略的点模型类建议独立成文件。ArkTS 对结构化类型检查比较严格把字段类型错误的数据传进组件编译阶段可能察觉不到但运行时很容易拿不到预期字段。model 层定义了清晰的数据契约比如 Spot 类里有 id、name、brief、desc、coverUrl、category 这些字段数据加载完转成数组之后页面用起来就非常安心。我在这个项目里是先把数据结构和页面框架都定好再填充具体内容的顺序对调的话会反复改代码。3. 页面是怎么做出来的ArkUI核心组件实操拆解3.1 首页卡片式列表和轮播图首页是冀游助手的门面我设计成三段式布局顶部一个横幅轮播区展示重点推荐中间是分类标题下面是一个可滚动的卡片列表。这个结构非常适合信息类应用因为它做到了“先吸引眼球、再引导阅读”的节奏。横幅轮播用的是 Swiper 组件ArkUI 的轮播组件内置了循环播放、自动切换、手势滑动等能力不需要自己写定时器。在 PC 上使用 Swiper 要特别注意两点一是宽度要设置为窗口宽度的百分比而不是固定像素这样窗口拉宽时才不会露馅二是高度要有一个合理的上限PC 屏幕是横向的轮播图太高会挤压下面的列表区域。我在项目里把它控制在 180 到 200 之间视觉上比较舒服。卡片列表的核心是 List 和 ListItem。ArkUI 的 List 容器支持懒加载机制配合 ForEach 遍历数据数组就能自动生成列表项。这里我要说一个非常重要的实操细节ForEach 的第三个参数“键值生成器”一定要写。它的作用是告诉框架“怎么判断一个列表项是新增、删除还是移动”如果不提供默认使用数组索引那列表的顺序变化时框架会认为所有项都没变。在实际项目里我用的是每一条数据独有的 id这样能减少很多不必要的渲染性能问题。import { Spot } from ../model/Spot; import { loadSpotData } from ../data/DataSource; Entry Component struct Index { State spots: Spot[] []; aboutToAppear(): void { this.spots loadSpotData(); } build() { Column() { Text(冀游助手) .fontSize(24) .fontWeight(FontWeight.Bold) .margin({ top: 16, bottom: 12 }) Swiper() { ForEach(this.spots, (item: Spot) { Image(item.coverUrl) .width(100%) .height(190) .objectFit(ImageFit.Cover) .borderRadius(12) }, (item: Spot) item.id) } .height(210) .loop(true) .autoPlay(true) .interval(3500) .indicator(true) .margin({ left: 16, right: 16 }) List({ space: 12 }) { ForEach(this.spots, (item: Spot) { ListItem() { this.SpotCardView(item) } }, (item: Spot) item.id) } .layoutWeight(1) .margin({ top: 12 }) .padding({ left: 16, right: 16 }) } .width(100%) .height(100%) .backgroundColor(#F5F6FA) } Builder SpotCardView(item: Spot) { Row() { Image(item.coverUrl) .width(120) .height(84) .borderRadius(8) .objectFit(ImageFit.Cover) Column({ space: 6 }) { Text(item.name) .fontSize(18) .fontWeight(FontWeight.Medium) Text(item.brief) .fontSize(14) .fontColor(#666666) .maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis }) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) .margin({ left: 12 }) } .width(100%) .padding(10) .backgroundColor(Color.White) .borderRadius(12) } }这套代码跑起来后首页就已经具备完整的信息流结构。这里有一个技巧ArkUI 的 Builder 修饰符可以把一段 UI 封装成一个方法在列表里通过 this.SpotCardView(item) 调用。这样写的好处是卡片视图可以复用于其他页面比如后面做的收藏页同样可以调用同一个 Builder 方法。3.2 详情页大图、图文介绍与收藏入口从首页点进详情页数据传递用的是路由参数。我在跳转的时候通过 router.pushUrl 把当前条目的 id 带过去详情页再根据这个 id 从全局数据源里取对应对象。这种“列表负责展示详情按 id 查询”的模式非常经典数据量不大时足够用而且逻辑清爽。详情页的布局是单列滚动顶部是大图标题和收藏按钮放在一行下面是一段图文长文。单列滚动在 PC 上也可以接受但要注意限制正文最大宽度否则一行文字会拉得特别长阅读体验很差。我做法是给正文加了一个 .constraintSize({ maxWidth: 860 }) 的限制在窗口特别宽的时候内容仍然集中在中间区域。收藏按钮是这个小项目里最有意思的状态管理场景。我用了 State 修饰一个布尔变量 isFavorite点击按钮后取反同时按钮的图标和文字都会跟着变化。刚开始我以为这就完了直到后来测试发现离开详情页再回来收藏状态丢了。这是因为页面销毁后 State 变量也随之消失。要真正持久化收藏状态需要用到持久化存储能力也就是鸿蒙的 AppStorage 配合 Preference 框架。正确做法是启动应用时先读取本地存储的收藏列表放进 AppStorage 作为全局状态详情页里每次点击收藏都同步写入全局状态和本地存储。这样即使杀掉应用再打开收藏记录依然在。实现收藏后还有一个交互细节已收藏的条目在首页卡片上应该显示一个小角标这时候首页也需要订阅这个全局状态。用 StorageLink 可以实现页面和全局存储的双向绑定首页读到的收藏列表每次变化时都会自动刷新完全不需要手动通知。这里我把核心的收藏逻辑写得很细用 Set 来存收藏的 id 集合判断某条是否已收藏时直接查集合时间复杂度是 O(1)。如果你用数组去存然后每次 indexOf虽然在这个数据规模下也不至于慢但从一开始就养成选对数据结构的习惯对后续项目只有好处。4. 数据从哪来静态数据组织与图片资源的正确处理4.1 把河北简介拆成结构化数据冀游助手的数据是纯静态的我把它们放在一个 JSON 文件里。这样做的考虑是想把项目的重点放在 UI 和交互上但如果连数据读取都做得不干净后面接真实接口时就得改一大堆。所以我按照真实接口的返回格式来设计 JSON页面代码只认数据模型不关心数据到底来自本地还是网络。JSON 的结构如下{ spots: [ { id: spot_001, name: 山海关, category: scenery, coverUrl: /resources/rawfile/images/shanhaiguan.webp, brief: 天下第一关, desc: 这里被誉为明长城的东部起点..., highlights: [古城墙, 海楼, 老龙头] }, { id: food_001, name: 驴肉火烧, category: food, coverUrl: /resources/rawfile/images/lvrouhuoshao.webp, brief: 街头巷尾的经典味道, desc: 外皮酥脆内馅鲜香..., highlights: [火烧, 驴肉, 焖子] } ] }数据加载有几种方式我最后选择了直接使用 import 引用 JSON。ArkTS 支持将 JSON 文件作为模块导入框架会自动解析成对象。初始化数据源的方法看起来很直接import data from ./hebei_data.json; export function loadSpotData(): Spot[] { const raw data as any; return raw.spots.map((item: any) { return new Spot(item); }); }这里还有一个严格类型的问题要提醒ArkTS 的编译检查比 JavaScript 要严格直接从抽象对象上取字段会报类型错误。所以我在 model 层里写了一个 Spot 构造函数负责从原始对象转换为类型安全的实例。这个过程在行业里叫数据清洗或对象映射写习惯了之后会觉得它特别重要尤其是团队协作时它相当于一条明确的数据边界。4.2 图片资源与多倍图适配PC窗口要注意什么图片是整个项目里最影响观感的部分。PC 屏幕上图片会被拉得比手机大得多如果原图分辨率不够放大后全是马赛克。我的经验是封面图最少要有 1280 像素宽度首屏轮播图则应该准备 1920 宽度的版本。这个要求不苛刻但确实需要提前注意。在项目里图片放到了 resources/rawfile 目录下程序运行时通过相对路径加载。注意 rawfile 的文件名和路径不能出现中文字符我踩过这个坑一开始想用“山海关.png”直接做文件名编译不报错但运行时表现不稳定。后来统一改成拼音或英文短横线命名问题就消失了。图片格式上我强烈建议用 WebP 而不是 PNG。同样的画质WebP 的文件大小经常只有 PNG 的三分之一不到。在 PC 端应用里一个几兆的 PNG 图片会明显拖慢应用安装包和首屏加载速度而 WebP 则完全没压力。你可以在资源目录里放两张同内容的图片对比一下体积差距肉眼可见。再说一点多倍图的概念。鸿蒙的 UI 单位是 vp在 PC 上 1vp 对应的物理像素可能超过手机的默认值。如果一张图片的 CSS 概念上的尺寸是 120vp在手机屏幕上也许只要 240 物理像素宽度的图就够了但在 PC 上可能需要 360 甚至 480 物理像素。我一般会在资源目录下建 multi-column 多分辨率适配的机制对低分辨率设备用 1xPC 用 2x。这样应用在不同设备上都能保持清晰不会出现模糊或者文件过大的尴尬。5. 调试、打包与真机效应把应用跑到PC上5.1 预览器与模拟器两种调试方式怎么搭配冀游助手开发过程中我用到了两种调试方式本地预览器 Previewer 和模拟器 Emulator。它们的定位完全不同。Previewer 最大的优势是快。我改一行代码几百毫秒之内界面就刷新了非常适合调间距、颜色、字体这些“看感觉”的东西。但它有几个致命问题不支持完整的路由跳转和系统能力调用而且在多窗口场景下表现不精确。我试过在 Previewer 里跑模拟器引导的完整流程结果是点击跳转根本没反应后来才知道 Previewer 只负责单页面渲染。模拟器则是一个完整的系统环境。冀游助手最终验收阶段全在模拟器上进行我会刻意调整窗口大小从 1280x720 拉到 1920x1080观察页面布局是否自适应。这也是 PC 开发和手机开发最大的差别手机屏幕尺寸是固定的而 PC 窗口可以被用户随意拖拽所以布局必须使用弹性布局和百分比宽度不能写死像素。我在项目里有一个专门用于验证自适应的方式把窗口拖到极端宽度比如 800 像素的窄窗口和 2400 像素的宽窗口然后逐个页面检查。第一次跑的时候详情页的正文在 2400 像素宽度下直接变成一行超长文本后来加了 maxWidth 限制才解决。这类问题不靠模拟器实测光看预览器永远发现不了。5.2 打包与安装HAP 是怎么生成的应用开发完成后最终要产出安装包。鸿蒙应用的安装包叫 HAP也就是 Harmony Ability Package。在 DevEco Studio 里构建流程非常透明点击菜单栏的 Build选择 Build Hap(s)/APP(s)如果只构建当前模块就直接生成对应模块的 HAP 包。构建之前必须处理签名问题。真机安装需要有效的签名证书模拟器则宽松一些。第一次接触签名的开发者可能会被各种 .p12 和 .cer 文件搞晕。简单说签名就是给应用打一个数字指纹确保安装包没有被篡改且来源可信。DevEco Studio 提供了自动签名模式你只需要登录账号并关联一个项目IDE 会自动帮你完成证书的申请和配置。我要提醒一点HAP 包直接性质类似于安卓的 APK但它不能直接双击安装需要通过 DevEco Studio 的 Run 按钮或者专门的命令行工具安装到模拟器或真机上。我用得最多的还是 IDE 的 Run 按钮点击后它会自动完成构建、签名、安装、启动这一条流程。调试时这么做最省心。5.3 调试踩坑实录那些必须写进备忘的问题第一个坑是列表滑动卡顿。刚写完首页的时候我在模拟器上测试滑动明显感觉不流畅。原因是我在 ForEach 里做了一些图片的复杂裁剪操作每次列表项进入可视区域都重新计算。解决方法是把图片裁切参数固定下来并把列表项的图片加载改为自适应尺寸的异步加载模式。对于数据量再大一些的场景则建议使用 LazyForEach 配合懒加载这样列表项真正进入屏幕前不会去初始化资源。第二个坑是路由跳转传参的类型丢失。我用 router.pushUrl 传递了一个对象结果详情页接收的时候打印出来是 undefined。查了半天才发现路由参数只能传递基本类型或者序列化后的字符串传对象需要先 JSON.stringify 再传到详情页再 JSON.parse。之后我就不再传整个对象了只传 id再通过数据源重新查询这样既规避了类型问题也符合数据驱动页面的原则。第三个坑是应用启动白屏。有一次我把 JSON 数据文件放在了 rawfile 外的位置导致 import 之后编译虽然通过但运行时冷启动找不到资源页面白屏。这个问题非常隐蔽因为编译器不报错。解决方法是把所有静态资源统一放在 rawfile 目录下并且在启动逻辑里加一个数据源加载失败的兜底提示这样出了问题至少屏幕上能直接看到原因而不是一片白。第四个坑我现在还记得很清楚模拟器冻结。当时的场景是项目里有一个 .gif 格式的动图作为加载动画在模拟器上转到某一步突然整个模拟器卡死。查下来是模拟器对 gif 的多帧解码支持不太好换成 WebP 动图后问题消失。这个经历让我养成了一个习惯涉及动图或者复杂动画先在真机上测试模拟器虽然方便但渲染能力毕竟和真机有差距。6. 回顾与扩展思路6.1 用下来的直接感受现在回看整个冀游助手的开发过程最大的体会是“在一个限定边界里做到完整”这件事比想象中更有价值。我没有把它做成一个功能堆砌的展示品而是从数据到页面到收藏交互都有闭环每一步都能讲清楚为什么这么做。这种感觉和以前写小 demo 完全不一样。ArkUI 这套声明式 UI 框架用熟了以后效率很高。它和传统的 XML 布局很不一样更像是把 UI 当函数处理状态一变界面自动更新省掉了大量的 DOM 操作代码。鸿蒙的文档虽然已经比较完善但很多细节还是要靠在实际调试中摸索好在 Previewer 反馈快踩坑成本低。6.2 接下来还能怎么扩展冀游助手目前的定位是本地静态数据但它的骨架已经具备接真实数据的基础。后续如果想升级第一个可以做的是把数据源切到远端 API通过模块化数据层替换掉本地的 import页面的其余部分完全不用动。第二个方向是加入地图能力HarmonyOS 提供了地图组件可以把景点列表渲染到地图上点击标注跳回详情页这在 PC 上会是一个很惊艳的体验。还有一个我一直在惦记的方向是深色模式。PC 用户对深色模式的需求明显比手机用户高尤其在晚上写代码或者调资料的时候。如果要做需要把颜色值统一收口到一个 Theme 类里而不是在每个组件里散布色号这样一套配色能反复切换。工作量和代码改动其实不太大但对应用质感的提升非常明显。个人而言这个项目让我对鸿蒙 PC 开发的整体情况有了底。现在无论再拿到什么信息展示类的需求我都能比较快地判断出用什么布局结构、会不会有性能风险、怎么组织数据。如果你也想用鸿蒙做点东西入门强烈建议从一个“简介展示应用”入手它麻雀虽小五脏俱全能让你完整走完从环境到打包全流程后面做正式项目时心里就不慌了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑