ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

鸿蒙5.0开发入门:从ArkTS到ArkUI的完整实战指南

鸿蒙5.0开发入门:从ArkTS到ArkUI的完整实战指南 简介面向鸿蒙5.0HarmonyOS NEXT初学者的App开发入门Demo将工程模板、构建配置与示例代码整合为可直接走通的实操包适合零基础或刚接触ArkTS/ets的开发者快速建立鸿蒙应用整体认知。压缩包共445个文件体积约1.84MB以js、json、ts、ets源码为主另含build-profile.json5、oh-package.json5等构建与包管理配置以及clang-format、gitignore等工程规范文件结构上覆盖从项目初始化到编译部署的完整链路。目前已有453人学习下载。通过对照AppScope目录、hvigor脚本和各模块配置读者可理解鸿蒙项目的分层组织方式掌握依赖锁定、代码检查与本地环境配置等关键环节并基于自带HAP和示例页面动手修改缩短从阅读文档到独立开发之间的距离。1. 为什么说鸿蒙5.0是App开发的一个分水岭做过几年移动端开发的朋友应该能明显感觉到鸿蒙5.0HarmonyOS 5.0和之前的版本有本质区别。往大了说它彻底放弃了兼容Android APK的路线从系统底层到应用框架全部走自研路线这意味着App开发逻辑跟传统的Android开发完全分道扬镳。往实际了说以前你写Android项目还能顺手打一个鸿蒙包现在这条路基本堵死了想在这个生态里做应用就必须重新学一套开发范式也就是DevEco Studio配合ArkTS语言、ArkUI声明式框架这一整套东西。这篇文章我准备以一个完整可运行的入门Demo为主线把鸿蒙5.0的工程结构、页面开发、状态管理、打包调试等核心环节都过一遍。这个Demo不需要多花哨但一定要跑得起来、看得懂、改得动适合刚接触鸿蒙生态的前端开发者、Android开发者以及想评估鸿蒙开发成本的技术负责人参考。我不会只贴代码还会把每一步背后的设计逻辑和坑点讲清楚。先说一个最直观的感受如果你有TypeScript或SwiftUI的基础学鸿蒙开发的曲线会平缓很多。ArkTS基于TypeScript做了静态类型强约束而ArkUI的布局写法跟声明式UI的思路几乎一脉相承。如果你只有传统Android XML或View系统的经验那需要先适应组件化、状态驱动的思维模式。别急下面从零开始带你把这套东西撸一遍。2. 开发环境搭建与工程结构解读2.1 DevEco Studio的下载与版本选择开发鸿蒙App的官方IDE是DevEco Studio本质上是基于IntelliJ IDEA定制的。下载地址在华为开发者官网这个没什么好绕弯的。有一点要注意鸿蒙5.0对应的是DevEco Studio 5.0以上版本SDK方面建议直接安装API 12或更高版本因为5.0系统全面转向了鸿蒙内核API级别太低会直接影响真机调试和部分系统能力调用。安装过程没什么特别Windows和macOS都有对应安装包按默认选项安装即可。安装完成后首次启动会让你配置SDK路径建议用默认路径省心。这里有个容易忽略的点DevEco Studio的SDK Manager里会分“Public SDK”和“Full SDK”一般开发用Public就够但如果你要调试系统接口、申请某些敏感权限就需要切换到Full SDK并签署相应协议。入门阶段不用碰Full SDK免得给自己挖坑。提示开发工具版本跟手机系统版本不匹配是常见问题后面专门用一节讲怎么排查。2.2 创建第一个工程模板选择与初始化打开DevEco Studio选择“Create Project”会出现多个模板。我们选“Empty Ability”就行这个模板生成最干净的Hello World工程方便看透目录结构。如果你选带TabBar的模板初始代码会多一些反而不利于理解底层脉络。工程创建过程中需要填Project Name如HarmonyDemo、Bundle name如com.example.harmonydemo这个Bundle name相当于应用的唯一标识类似Android的applicationId创建后不好改想清楚再填。Project type选“Application”其他选项默认点击Finish后IDE就开始自动同步工程第一次会比较慢因为要下载依赖组件吃个饭再回来看都正常。等待同步完成后你会看到这样的核心目录结构HarmonyDemo/ ├── AppScope/ │ ├── app.json5 # 应用全局配置 │ └── resources/ # 应用级资源 ├── entry/ # 应用的主模块 │ ├── src/ │ │ ├── main/ │ │ │ ├── ets/ # 源码目录 │ │ │ │ ├── entryability/ # Ability生命周期逻辑 │ │ │ │ └── pages/ # 页面文件 │ │ │ ├── resources/ # 模块资源 │ │ │ └── module.json5 # 模块配置 │ ├── build-profile.json5 # 构建配置 │ └── hvigorfile.ts # 构建脚本入口 ├── oh-package.json5 # 依赖声明 └── build-profile.json5 # 全局构建配置简单来说entry代表应用的主模块Android里你可能叫app模块性质一样。module.json5里声明了Ability、权限、设备类型等关键信息相当于AndroidManifest.xml和build.gradle的部分合体。2.3 工程关键配置module.json5与app.json5module.json5是每个模块的核心配置入门需要关注这几个字段{ module: { name: entry, type: entry, // 模块类型entry代表应用入口模块 description: $string:module_desc, mainElement: EntryAbility, // 入口Ability deviceTypes: [phone, tablet], abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ts, description: $string:EntryAbility_desc, icon: $media:icon, label: $string:EntryAbility_label, startWindowIcon: $media:startIcon, startWindowBackground: $color:start_window_background, exported: true, skills: [ { entities: [entity.system.home], actions: [action.system.home] } ] } ] } }不用强记但理解两个点第一srcEntry指向Ability入口文件这是应用启动时加载的第一个代码文件第二skills里的action.system.home是应用图标的入口配置类似于Android的ACTION_MAIN和CATEGORY_LAUNCHER组合。改错这里会导致应用不出现在桌面上排查时记得看一眼。app.json5则管应用级信息比如应用名称、版本号versionCode和versionName、SDK兼容版本等。版本号这里容易踩坑因为鸿蒙有compatibleSdkVersion和targetSdkVersion两个概念前者决定最低可运行版本后者决定目标版本配置不当会直接编译失败。3. ArkTS与ArkUI鸿蒙开发的核心语言与框架3.1 ArkTS为什么强调“静态类型”ArkTS不是一门全新的语言它是TypeScript的超集在TS基础上强化了静态类型检查和运行时约束。为什么华为要这么干一个重要的原因是移动端性能。TypeScript本身是弱约束的解释型语言面向UI这种高频交互场景如果类型全靠运行时推断性能和稳定性都会打折扣。ArkTS相当于在编译阶段就把绝大多数类型问题拦在门外减少运行时错误。对开发者来说这意味着写代码时更“束手束脚”不能随意any一把梭不能像JS那样灵活地动态增删对象属性。一开始会觉得不习惯比如这种情况在JS里没问题但ArkTS会直接报错// 错误示例ArkTS不允许动态添加属性 let person { name: Tom }; person.age 18; // 编译报错正确做法是定义interface明确结构interface Person { name: string; age?: number; // 可选属性 } let person: Person { name: Tom }; person.age 18;一句话总结把TypeScript里那些“比较容易钻空子”的写法堵死用更严格的规范换性能和稳定。写惯强类型语言的人适应起来很快写惯JS的人前期会多踩几个编译报错习惯了也就好了。3.2 ArkUI声明式UI的核心概念ArkUI是整个鸿蒙UI框架的名字核心思想是“描述UI状态而非手动操作UI”。你只需要声明组件树和它们的数据依赖关系数据变化时框架自动刷新界面。听上去是不是很像SwiftUI或者Flutter没错就是这个路子。一个最简页面长这样Entry Component struct Index { State message: string Hello HarmonyOS; build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) Button(点击更新) .onClick(() { this.message Hello ArkTS!; }) } .width(100%) } .height(100%) } }几个关键装饰器要搞清楚Entry标记当前组件为页面入口一个页面文件只有能有一个。Component标记这是一个自定义组件可以组合其他组件。State标记这是一个状态变量当它的值变化时界面会自动刷新。struct定义组件结构体的关键字类似class但功能上有差异。这个“声明式”的思维转变很重要。以前写Android是findViewById拿到控件再setText更新内容也就是命令式操作。现在你只需要把数据和UI绑定起来状态的变更由框架接管。刚开始不适应但写过两个页面之后就会觉得这种方式写UI效率高很多尤其是列表、表单这类频繁变更的场景。3.3 基础组件与布局方式速览ArkUI的布局核心是“容器组件子组件”的嵌套关系类似Android的ViewGroup。入门阶段最常用的几个Row水平排列子组件Column垂直排列子组件Stack层叠排列子组件可以重叠ListListItem高效长列表Grid网格布局类似Android的RecyclerView GridLayoutManager基础控件方面则包括Text文本、Button按钮、Image图片、TextInput输入框、Toggle开关等命名和React Native、Flutter里的大同小异。布局属性上有几个高频用法要记住// 通用布局属性 .width(100%) // 宽度 .height(200) // 高度数字默认vp单位 .backgroundColor(#f0f0f0) .padding(16) // 内边距 .margin({ top: 8, bottom: 8 }) // 外边距 .borderRadius(8) // 圆角 .alignItems(HorizontalAlign.Center) // 子组件对齐方式 .justifyContent(FlexAlign.SpaceBetween) // 主轴上分布方式这里有一个单位的概念默认数字单位是vpvirtual pixel类似Android的dp适配不同屏幕密度的相对单位。字体大小同理用fpfont pixel会自动跟随系统字体缩放设置。这个细节不搞清楚后续适配会吃亏。4. 实操从零编写一个可运行的鸿蒙入门Demo4.1 Demo的需求定位与页面规划既然叫入门Demo就别贪多我把它设计成一个“待办事项”应用。为什么选这个因为它涵盖了开发中最常用的几个能力列表展示、输入交互、状态管理、页面跳转、动态增删数据。把这些跑通你基本就掌握了一个鸿蒙App开发的完整闭环。功能拆成两个页面首页Index展示待办列表底部有一个输入框和添加按钮可以动态添加待办事项点击事项可以删除。详情页Detail展示从首页传递过来的具体事项内容模拟一个典型的参数传递场景。这个规模对于第一次接触鸿蒙开发的人来说刚刚好不会因为功能太多而迷失在代码里又能完整体验一个App从数据到界面的完整链路。4.2 数据模型与全局状态设计在ArkTS中数据模型推荐用class或interface描述配合State、Observed等装饰器实现响应式。我们的待办事项数据做一个简单的class// model/TodoItem.ets Observed export class TodoItem { id: number; title: string; isDone: boolean false; constructor(id: number, title: string) { this.id id; this.title title; } }Observed装饰器的含义是“这个类的属性变化会被观察”配合ObjectLink可以在父子组件之间建立细粒度的数据关联。但这里有一个新手容易搞混的点State和Observed的区别。简单来说State是用在组件里修饰普通类型变量的string、number、boolean、普通数组等Observed是修饰class本身的让其实例可以被观察。如果class没有加Observed即使组件里用State接收了它的实例修改内部属性也不会触发UI刷新。不过在我们的Demo中为了避免一开始就引入太复杂的装饰器组合可以在页面内部直接用State管理一个数组。真正的项目里再逐步引入Observed和ObjectLink来拆分组件入门阶段先保证跑通。4.3 首页开发列表展示与输入交互页面默认生成在entry/src/main/ets/pages/Index.ets我们直接改造它。完整代码如下import { TodoItem } from ../model/TodoItem; Entry Component struct Index { State todoList: TodoItem[] [ new TodoItem(1, 学习鸿蒙ArkTS语法), new TodoItem(2, 搭建开发环境), new TodoItem(3, 跑通第一个Demo) ]; State inputValue: string ; private nextId: number 4; build() { Column() { // 标题栏 Text(我的待办) .fontSize(24) .fontWeight(FontWeight.Bold) .width(100%) .padding(16) .backgroundColor(#F1F3F5) // 待办列表 List({ space: 12 }) { ForEach(this.todoList, (item: TodoItem) { ListItem() { Row() { Text(item.title) .fontSize(16) .decoration({ type: item.isDone ? TextDecorationType.LineThrough : TextDecorationType.None, color: #999999 }) .layoutWeight(1) Button(item.isDone ? 已完成 : 标记完成) .fontSize(12) .height(32) .type(item.isDone ? ButtonType.Normal : ButtonType.Capsule) .onClick(() { item.isDone !item.isDone; }) } .padding(12) .backgroundColor(#FFFFFF) .borderRadius(8) .shadow({ radius: 4, color: #11000000, offsetY: 2 }) } .onClick(() { this.deleteTodo(item.id); }) }, (item: TodoItem) item.id.toString()) } .layoutWeight(1) .width(100%) .padding({ left: 16, right: 16, top: 8 }) // 底部输入区 Row() { TextInput({ placeholder: 输入新的待办..., text: this.inputValue }) .layoutWeight(1) .height(44) .backgroundColor(#F1F3F5) .borderRadius(22) .padding({ left: 16, right: 16 }) .onChange((value: string) { this.inputValue value; }) Button(添加) .height(44) .margin({ left: 8 }) .onClick(() { this.addTodo(); }) } .width(100%) .padding(12) .backgroundColor(#FFFFFF) } .width(100%) .height(100%) .backgroundColor(#F7F8FA) } addTodo() { const title this.inputValue.trim(); if (title ) { return; } this.todoList.push(new TodoItem(this.nextId, title)); this.nextId; this.inputValue ; } deleteTodo(id: number) { this.todoList this.todoList.filter((item: TodoItem) item.id ! id); } }这段代码里有几个值得注意的点。ForEach的第一个参数是数据源数组第二个是生成每一项子组件的函数第三个是key生成函数。key一定要稳定唯一这里用item.id.toString()注意第二参数类型是(item: TodoItem, index?: number) void不是简单的回调。如果没有第三个参数框架会默认用index作为key这样列表删除或排序时可能出现UI复用错乱。列表项的删除我直接绑定了ListItem的onClick但这个设计有一点不合理因为“标记完成”按钮也在这个列表项里点击按钮时事件会冒泡导致按钮操作完紧接着触发了删除。所以实际运行时你会发现一个按钮点击同时触发了两个动作。修正方法是给按钮的点击事件加stopPropagation()代码里调整一下Button(...) .onClick((event: ClickEvent) { event.stopPropagation(); item.isDone !item.isDone; })这种事件冒泡问题在新手阶段非常隐蔽因为编译不报错逻辑也能跑但行为完全不对。这也是我强烈建议做Demo时边运行边调试别一口气写完再统一断言的另一个原因。4.4 详情页开发页面跳转与参数传递再来一个详情页接收从首页传过来的标题文本。新建文件entry/src/main/ets/pages/Detail.etsEntry Component struct Detail { State title: string ; aboutToAppear(): void { // 从路由参数中获取title const params router.getParams() as Recordstring, string; if (params params.title) { this.title params.title; } } build() { Column() { Text(详情页) .fontSize(24) .fontWeight(FontWeight.Bold) .width(100%) .padding(16) .backgroundColor(#F1F3F5) Text(this.title) .fontSize(20) .margin({ top: 40, left: 20, right: 20 }) .width(100%) Blank() Button(返回) .width(80%) .onClick(() { router.back(); }) .margin({ bottom: 40 }) } .width(100%) .height(100%) .backgroundColor(#F7F8FA) } }对应的首页里如果要跳转到这个详情页需要引入路由API并把title传过去import { router } from kit.ArkUI; // 在某个按钮的点击事件里 router.pushUrl({ url: pages/Detail, params: { title: item.title } });这里有个细节router.pushUrl的url路径是pages/Detail不需要后缀.ets这个路径是相对于entry/src/main/ets目录的。如果页面文件放在子目录下路径要对应写全比如pages/second/Detail。另外鸿蒙的router和Navigation是两套路由体系router偏传统、简单直接Navigation更现代支持嵌套容器、自定义转场动画、跨包路由等官方也在逐步推荐用Navigation。新手入门用router最容易理解项目大了再考虑迁移Navigation不迟。4.5 关于命名与代码组织的几点建议入门Demo的代码量不大全部塞到pages下也勉强可以但一旦页面多了这堆文件就会变成灾难。建议从Day 1就按职责分目录entry/src/main/ets/ ├── entryability/ # Ability相关 ├── pages/ # 路由页面 ├── model/ # 数据模型 ├── viewmodel/ # 状态管理逻辑可选 ├── components/ # 可复用的自定义组件 └── common/ # 常量、工具函数这不是强制要求但文件归类越清晰后面找东西越省心。很多人把项目写烂不是因为一开始逻辑多复杂而是因为目录太乱改一个需求要翻半天文件。5. 构建产物HAP、HSP、HAR到底是什么5.1 三类包体的定位与区别鸿蒙生态的包类型比Android多一点新手首先要分清HAP、HSP、HAR三个概念。可以类比着记忆HAPHarmonyOS Ability Package应用安装包相当于Android里的APK。它包含一个模块的代码、资源和配置文件是最终安装到设备上的基本单元。HARHarmonyOS Archive静态共享包类似Android的AAR或Java的JAR。代码和资源被编译进宿主HAP不能独立运行也没自己的Ability。HSPHarmonyOS Shared Package动态共享包类似Android的动态模块或插件化方案。运行时按需加载多个HAP可以共享同一份HSP用来做模块化、动态下发都很方便。用一张表快速对比类型能否安装运行复用范围典型使用场景HAP能单模块App入口模块、功能模块HAR不能编译期静态复用工具库、UI组件库、基础能力封装HSP不能独立安装运行期按需加载动态功能模块、多渠道共享业务代码对一个入门Demo来说你最终打出来的就是HAP包。默认模板还会在oh-package.json5里带一些HAR依赖比如路由、工具库本质就是引入静态共享包来复用别人的能力。5.2 如何打出一个可安装的HAP包在DevEco Studio里打包入口在菜单栏“Build”有两个选项需要搞清楚Build Hap(s)/APP(s) - Build Hap(s)只构建HAP包用于真机调试或本地自测。Build Hap(s)/APP(s) - Build APP(s)构建一个.APP文件这是应用市场的上架格式里面可以包含多个HAP/HSP。调试阶段我们主要用“Build Hap(s)”。默认的输出目录在模块下的build/outputs/default/你会看到类似entry-default-signed.hap的文件。带signed说明签名过可以直接往手机上装不带的说明没签名装不上。提示如果你看不到输出包或编译报错签名问题优先检查Build菜单里的“Signing Configs”确认是否配置了自动签名。登录华为账号后DevEco通常会自动生成一个调试证书省去手动配置Profile的麻烦。5.3 关于“可以打包成hap、hsp、har的鸿蒙demo”你可能会搜到一些项目说“支持打出hap、hsp、har三种包”这种demo一般是把一个简单库函数分别以静态共享包HAR和动态共享包HSP形式提供再用主模块HAP依赖它们最后展示三种产物的打包方法。对于入门理解模块化工程很有价值但不要在第一个练手项目里就强行上这种多模块结构。先把单模块的HAP跑明白再拆HAR、HSP路线更稳妥。6. 真机调试与签名配置专项6.1 真机调试的前置条件鸿蒙的模拟器跑起来比较慢而且有些传感器能力模拟不了所以强烈建议用真机调试。前置条件有四个手机开启“开发者模式”在“设置-关于本机”里连续点击“版本号”7次。在“系统和更新-开发人员选项”里打开“USB调试”。用USB数据线连接电脑手机上弹出授权窗口时允许调试。DevEco Studio的“File - Project Structure - Signing Configs”里勾选“Automatically generate signature”并登录华为账号。前三个条件和Android调试基本一致第四个是鸿蒙特有的签名要求。鸿蒙系统对应用签名校验很严格没有签名或签名不匹配应用根本装不上去。6.2 开发时cli与手机端版本不同怎么解决这是很多新手在真机调试时被卡住最多的地方。具体表现是DevEco Studio的hdc工具能识别到设备但安装时或者运行时提示版本不匹配、SDK不兼容、连接失败等。常见的坑有这几种第一DevEco Studio版本太老识别不了新手机。手机系统升级到鸿蒙5.0之后旧版DevEco带的SDK和hdc跟不上新系统的通讯协议表现为设备连接不上或设备列表为空。解决办法是升级DevEco Studio到5.0及以上版本升级SDK到API 12及以上。第二手机系统和工程配置的targetSdkVersion不一致。工程的compileSdkVersion最好不低于真机系统对应的API版本。你可以在项目build-profile.json5里查看当前的compileSdkVersion在真机的“设置-关于本机”里确认系统版本对应的API等级两者偏差过大就会出现兼容性问题。第三hdc版本不一致。命令行工具的hdc版本跟IDE内置的版本不一致时会出现连接成功后无法安装、无法启动调试进程的问题。在DevEco的Terminal里执行hdc version看一下输出然后和SDK目录下hdc的版本对比。如果不一致把命令行使用的hdc路径切换到DevEco安装目录下的SDK工具路径或者直接卸载重装命令行工具。第四签名证书和设备的API级别不匹配。自动签名生成的Profile如果只在某个API级别下有效手机系统升级后可能失效。这种情况在“Signing Configs”里重新生成一次签名即可。总的来说核心思路就一句保证IDE、SDK、手机系统三者版本对齐。版本越新越不容易出兼容问题不要为了省事拖着老版本不升级。6.3 模拟器调试的注意事项如果你的真机不方便连也可以用模拟器。DevEco Studio的Device Manager里可以下载Phone模拟器镜像。需要注意两点模拟器对电脑配置要求不低至少16GB内存才比较流畅模拟器运行的是x86镜像少数真机上正常的功能在模拟器上可能有细微差异比如某些传感器、定位能力。所以模拟器适合快速验证UI和逻辑最终上线前还是得真机过一遍。7. 常见编译与运行问题排查速查写鸿蒙Demo的初期我几乎每个环节都踩过坑有些问题搜遍全网也找不到满意答案只能自己翻日志和源码。这里把常见问题整理出一份速查表帮大家少走弯路。问题现象可能原因排查思路与解决工程同步失败ohpm下载超时网络原因或镜像源不稳定在oh-package.json5里检查依赖换用国内镜像源若依赖来自华为仓库还是超时关闭代理或换网络编译报错“undefined symbol”页面或组件引用了不存在的变量/方法检查import路径是否写全是否忘了引入kit.ArkUI等基础依赖预览器白屏组件树渲染异常打开Previewer的日志面板重点看是否有Entry重复、require循环等提示确认页面所在路径和编译产物一致模拟器卡顿电脑内存不足或镜像版本问题关闭其他大型应用清理模拟器缓存更建议直接切真机调试真机显示“安装失败”签名错误或包不一致确认“Automatically generate signature”已勾选删除手机上之前的旧应用再安装检查设备API等级List无法滚动布局容器给了固定高度但没有指定可滚动方向List需要约束高度同时在Column里使用layoutWeight(1)占满剩余空间状态更新不刷新State修饰的对象内部属性变更如果是class实例需要给class加Observed组件里用ObjectLink接收或者直接替换整个对象引用TextInput输入卡顿绑定了重量级状态输入事件不要触发大数组的重排尽量拆分组件让TextInput自身状态独立字体显示异常/布局挤变单位混用或未适配间距、尺寸统一用vp字体用fp不要直接套用Android dp/px的数值这几个问题里状态不刷新是最影响开发效率的因为逻辑上觉得没问题但界面就是不动。核心解决思路就两条要么把需要观察的类用Observed装饰要么每次修改数据时重新赋一个新数组引用。比如删除待办时写this.todoList this.todoList.filter(...)而不是this.todoList.splice(...)这样才能让框架检测到数组引用变化。8. 运行第一个Demo时的心得与扩展建议到这里你手里的Demo已经具备了一个App的完整雏形有页面、有数据、有跳转、有交互、能打包、能调试。把这套流程完整走一遍鸿蒙开发的基本功就算是有了三分之一。剩下的大头是深入学习ArkUI的高级组件和动画系统、熟练掌握状态管理框架比如V1/V2两种状态管理模型的选择、理解Ability框架的启动模式和任务栈逻辑。分享一个我实际体会比较深的点鸿蒙的几个装饰器机制尤其是State、Prop、Link、Provide、Consume这一套是理解整个响应式UI的关键。入门的时候往往只用到State但一旦页面结构复杂、组件需要共享状态时光靠State就会把代码写得很痛苦。建议下一步就系统过一遍状态管理相关的官方文档配合官方的“状态管理”Demo逐个测试这个投入的回报率非常高。最后分享两个小技巧。第一在DevEco Studio里多用Previewer它能边改代码边看效果比反复上真机快很多但遇到路由跳转、系统能力调用时Previewer支持有限还是要走真机。第二多看系统自带的示例工程DevEco Studio新建工程时自带的模板代码就是最正宗的最佳实践很多组件的用法直接参考模板比自己瞎试省力不少。鸿蒙5.0这个生态才刚进入快速发展期对开发者来说既是新挑战也是新机会。趁早把这套工具链和语言栈摸熟无论从技术积累还是职业发展的角度都是一件值得做的事。本文还有配套的精品资源点击获取
返回列表