鸿蒙开发入门指南前端开发者快速掌握一多应用的整体开发一、一多到底是什么前端开发者如何理解二、界面级一多页面适配前端最熟悉的部分断点 媒体查询 动态监听栅格布局 CSS Grid 响应式列数 动态排序侧边栏容器 写好的 Drawer 响应式控制交互归一不用操心鼠标还是手指自适应布局的几个组件三、功能级一多设备能力不一样怎么办四、工程级一多代码怎么组织才能一套代码多端部署两种部署模型应用程序包结构五、用一个简单例子串一遍再聊聊窗口适配和折叠屏窗口适配分屏和悬浮窗的坑折叠屏适配总结前端开发者的一多学习路线大家好我是木斯佳。今天咱们聊聊鸿蒙的「一次开发多端部署」官网叫「一多」。说实话第一次看到这个词我比较熟悉感觉是不是类似于Flutter、uniapp那种跨端方案读完文档才发现——这不就是咱们前端干了好几年的「响应式 跨端适配」吗只不过鸿蒙把它从「自己动手」变成了「系统兜底」。一、一多到底是什么前端开发者如何理解官方定义我就不复读了核心意思就是你写一套代码鸿蒙帮你跑到其生态手机、平板、PC、手表上而且看起来比较流畅。鸿蒙提出这个的背景也很现实——现在每个人手里的设备越来越多手机、平板、电脑、手表、车机…用户希望的是「以场景为中心」而不是「以设备为中心」。比如你在手机上看视频回到家想无缝切换到平板上继续看。这对开发者来说意味着什么你的应用得能在各种设备上跑而且体验不能打折。但问题来了如果每个设备都单独适配开发成本直接翻倍。鸿蒙的「一多」就是想解决这个问题。咱们前端做响应式靠的是媒体查询 flex/grid 各种断点判断。鸿蒙的思路差不多但区别在于前端你自己写一堆 CSS 媒体查询在不同屏幕宽度下调整布局鸿蒙系统提供了断点监听、栅格布局、侧边栏容器这些东西你只需要声明「在不同情况下怎么显示」说白了鸿蒙把「适配」这件事从「手写策略」变成了「配置规则」。为了实现「一套代码工程一次开发上架多端按需部署」官方文档里明确说要解决三个核心问题页面怎么适配不同屏幕尺寸和色彩风格—— 界面级一多功能怎么兼容不同设备的系统能力比如 NFC、SIM 卡手表上根本没有—— 功能级一多代码工程怎么组织才能一套代码多端部署—— 工程级一多下面咱们一个一个聊用前端的语言翻译一下。二、界面级一多页面适配前端最熟悉的部分界面适配是前端开发者最不陌生的部分。鸿蒙在这块提供了断点系统、栅格布局、自适应组件、交互归一这几样东西。断点 媒体查询 动态监听官网把设备窗口宽度分成五档但天气案例里主要用了三档鸿蒙叫法宽度范围前端类比sm320-600vpmedia (min-width: 320px) and (max-width: 600px)md600-840vpmedia (min-width: 600px) and (max-width: 840px)lg840vpmedia (min-width: 840px)代码写法// 鸿蒙写法letlistenerthis.getUIContext().getMediaQuery().matchMediaSync((840vpwidth));listener.on(change,(result){if(result.matches){this.curBplg;this.showSideBartrue;// 大屏显示侧边栏}});如果换成前端思维这相当于// 前端类比constmqlwindow.matchMedia((min-width: 840px));mql.addEventListener(change,(e){if(e.matches){this.curBplg;this.showSideBartrue;}});写法几乎一样只是鸿蒙把window换成了getUIContext()。但鸿蒙多了一步断点变化后你可以直接把状态存到AppStorage里任何组件都能订阅到AppStorage.setOrCreate(curBp,this.curBp);// 别的组件里StorageLink(curBp)curBp:stringlg;这有点像 Vue 的provide/inject或者 React 的Context但更轻量不需要手动传 props。栅格布局 CSS Grid 响应式列数 动态排序如果你用过 Element Plus 的el-row和el-col鸿蒙的GridRow/GridCol你闭着眼都能写。Element Plus 版本el-row :gutter20 el-col :xs24 :sm12 :md8 :lg6 内容 /el-col /el-row鸿蒙版本GridRow({columns:{xs:4,sm:8,md:12,lg:12},gutter:{x:20,y:20}}){GridCol({span:{xs:4,sm:4,md:3,lg:3}}){this.Content()}}看着差不多对吧但鸿蒙有两个前端没有的杀手锏。杀手锏一order可以按断点动态变化官网天气案例里有个需求大屏无侧边栏时「每日天气」和「空气质量」两个模块要互换位置。// 每日天气模块GridCol({span:4,order:{xs:2,// 手机第2个sm:2,// 小屏第2个md:2,// 中屏第2个lg:this.showSideBar?2:3// 大屏如果隐藏侧边栏就排第3}}){MultidayWeather/}// 空气质量模块GridCol({span:4,order:{xs:3,sm:3,md:3,lg:this.showSideBar?3:2// 大屏如果隐藏侧边栏就排第2互换}}){AirQuality/}这在前端得写多少个媒体查询至少 4 个。鸿蒙一个order属性搞定。杀手锏二columns可以按断点动态变化GridRow({columns:{xs:4,// 手机4列sm:8,// 小屏8列md:12,// 中屏12列lg:12// 大屏12列}})前端的 CSS Grid 虽然也能做但需要配合media重新定义grid-template-columns代码量翻倍。侧边栏容器 写好的 Drawer 响应式控制官网天气案例的大屏设备有个侧边栏用来切换城市小屏设备自动隐藏。鸿蒙直接用SideBarContainerSideBarContainer(SideBarContainerType.Embed){SideContent/// 左边栏Column// 主内容区TitleBar/Swiper.../Swiper/Column}.sideBarWidth(33.3%).showSideBar(this.showSideBar)这个showSideBar就是前面断点监听里动态改的那个状态。小屏设备设置false侧边栏就没了。前端要实现同样的效果你得写一个 Drawer 组件监听窗口宽度宽度小于阈值时隐藏 Drawer处理动画、遮罩、点击外部关闭…鸿蒙一个组件全包了。交互归一不用操心鼠标还是手指多设备意味着多种输入方式——手机用手指戳PC 用鼠标点电视用遥控器按。鸿蒙的「交互归一」把这几种输入统一成标准化事件开发者只需要写一套交互逻辑。官方文档里给了一张图看完就明白触控屏 → 触摸事件 鼠标 → 点击事件 → 统一交互接口 → 组件响应 键盘 → 按键事件 遥控器 → 方向键事件你不需要判断当前设备是触屏还是鼠标直接写onClick就行鸿蒙帮你转换。自适应布局的几个组件除了栅格鸿蒙还提供了几个专门用来做自适应的小组件Scroll内容超出时滚动解决小窗口内容显示不全的问题layoutWeight按权重分配剩余空间类似 CSS 的flex: 1displayPriority设置显示优先级空间不足时自动隐藏优先级低的元素// layoutWeight 示例三个子元素按 1:2:3 分配宽度Row(){Text(菜单).layoutWeight(1)Text(内容).layoutWeight(2)Text(侧边栏).layoutWeight(3)}// displayPriority 示例空间不足时先隐藏次要内容Text(标题).displayPriority(1)// 最重要一定显示Text(副标题).displayPriority(2)// 次要空间不够时隐藏Text(装饰文字).displayPriority(3)// 最次要最先隐藏三、功能级一多设备能力不一样怎么办这是前端不太会遇到的问题——你写的网页不管在哪个设备上打开浏览器 API 基本都一样。但鸿蒙不一样手表上没有 GPS车机上没有 SIM 卡直接调 API 会崩。鸿蒙把每个独立功能蓝牙、WiFi、NFC、摄像头等叫做一个 SystemCapabilitySysCap。不同设备支持的 SysCap 不同。解决方案有两个方案一安装时检查在module.json5里声明你的应用需要哪些系统能力不支持的设备根本装不上你的应用。{module:{requestPermissions:[{name:ohos.permission.CAMERA},{name:ohos.permission.NFC}]}}方案二运行时检测调用 API 之前先问一下系统有没有这个能力if(canIUse(SystemCapability.Location.Geocoder)){// 有定位能力自动获取城市this.getLocation();}else{// 没有定位让用户手动输入this.showCityPickertrue;}前端做类似的事情if(geolocationinnavigator){navigator.geolocation.getCurrentPosition(success,error);}else{this.showManualInputtrue;}思路完全一样只是 API 名字不同。官方文档还提到一个细节工程默认的要求能力集是多个设备支持能力的交集保证所有目标设备都能跑默认的联想能力集是并集开发时能用到所有 API。这意味着你写代码时可以调用高端设备才有的 API但调用前必须用canIUse判断否则低端设备会崩。四、工程级一多代码怎么组织才能一套代码多端部署这是我觉得鸿蒙做得比前端生态好的地方——它在框架层面就规定了代码组织方式而不是让每个团队自己折腾一套。官网推荐的三层架构application/ ├── common/ # 公共能力层编译成 HAR 包 ├── features/ # 基础特性层编译成 HAR/HSP/Feature HAP └── products/ # 产品定制层编译成 Entry HAP ├── phone/ # 手机版 ├── tablet/ # 平板版 └── pc/ # PC版用前端的思维理解common就是utils/、components/、api/抽成 npm 包features就是业务模块比如user/、order/、weather/每个模块可以独立打包products就是不同的入口比如main.js手机、main-pc.jsPC关键约束common不能依赖featuresfeatures不能依赖products——单向依赖避免循环引用。这个约束在前端要靠 ESLint 插件 团队规范来保证鸿蒙在架构层面就给你卡死了。两种部署模型官方文档里提到了两个概念部署模型 A 和部署模型 B。部署模型 A一次编译生成相同的 HAP 包所有设备跑同一套代码。适用于相同泛类的设备比如手机和平板。部署模型 B一次编译生成不同的 HAP 包不同设备跑不同代码。适用于不同泛类的设备比如手机和手表布局差异太大硬适配成本反而更高。官方建议相同泛类用模型 A不同泛类用模型 B。实际上大多数项目是两者混合使用。一个重要的提醒官方文档明确说了小屏幕场景如手表和屏幕比例特殊的场景如 PuraX 外屏由于应用设计布局差异显著无法通过一套布局调整实现适配。该写两套就写两套不要硬适配。应用程序包结构理解鸿蒙的包结构对组织代码很有帮助HAP应用安装的基本单位分为 Entry主模块和 Feature动态特性模块HAR静态共享包类似 npm 包被依赖时会打包到 HAP 里HSP动态共享包多个 HAP 可以共享同一份代码减少包体积一个应用最终打包成 APP Pack里面可以包含多个 HAP。这种设计的好处是用户可以只下载基础功能用到某个特性时再动态下载对应的 Feature HAP。五、用一个简单例子串一遍再聊聊窗口适配和折叠屏光说不练假把式。咱们手写一个极简版的「一多」Demo把核心概念串一遍。需求做一个卡片列表页面手机单列显示平板双列显示PC三列显示并且左侧加一个侧边栏第一步监听断点Componentstruct HomePage{StatecurBp:stringmd;StateshowSideBar:booleanfalse;aboutToAppear(){letlgListenerthis.getUIContext().getMediaQuery().matchMediaSync((840vpwidth));lgListener.on(change,(result){if(result.matches){this.curBplg;this.showSideBartrue;// PC显示侧边栏}else{this.curBpmd;this.showSideBarfalse;}});}}第二步用 SideBarContainer 包起来build(){SideBarContainer(SideBarContainerType.Embed){// 侧边栏Column(){ForEach([首页,发现,我的],(item){Text(item).fontSize(18).padding(12)})}.width(100%)// 主内容区Column(){this.CardGrid()}}.sideBarWidth(240vp).showSideBar(this.showSideBar)}第三步栅格实现响应式卡片BuilderCardGrid(){GridRow({columns:{xs:1,md:2,lg:3},gutter:{x:16,y:16}}){ForEach([1,2,3,4,5,6],(item){GridCol({span:1}){Column(){Text(卡片${item}).fontSize(18)Text(这是一段描述文字).fontSize(14).fontColor(#666)}.padding(16).backgroundColor(#fff).borderRadius(8)}})}.padding(16)}就这么几行代码手机、平板、PC 都能正常显示。窗口适配分屏和悬浮窗的坑除了屏幕尺寸还要考虑窗口模式。官网文档里详细介绍了不同设备支持的窗口模式设备默认模式支持的模式手机全屏全屏、分屏、悬浮窗平板全屏全屏、分屏、自由多窗、悬浮窗PC自由多窗全屏、分屏、自由多窗、悬浮窗当应用进入分屏或悬浮窗模式时窗口尺寸变化可能出现内容截断的问题。官网给出了几个典型案例问题一固定高度的组件超出父容器// 错误固定高度Column().height(500)// 正确使用 Scroll layoutWeightScroll(){Column().layoutWeight(1)}.height(100%)问题二Video 组件被截断// 错误直接设置宽高100%Video().width(100%).height(100%)// 正确设置 objectFit 保持比例Video().objectFit(ImageFit.Contain)问题三沉浸式页面顶部被悬浮窗控制条遮挡需要监听窗口避让区域变化动态调整顶部内边距this.windowClass.on(avoidAreaChange,(data){if(data.typewindow.AvoidAreaType.TYPE_SYSTEM){lettopHeightpx2vp(data.area.topRect.height);this.topPaddingtopHeight;// 动态设置顶部内边距}})折叠屏适配折叠屏有个特殊场景——悬停态。用户把手机半折立在桌面上上半屏显示内容下半屏显示操作面板。官方文档强调了几点展开态和折叠态切换时任务要连续不能闪退或重启悬停态适合视频通话、拍照、播放视频这些不需要频繁交互的场景不要为了凑内容强制应用分屏具体实现需要监听折叠状态变化display.on(foldStatusChange,(status){if(statusdisplay.FoldStatus.FOLDED){// 折叠态}elseif(statusdisplay.FoldStatus.EXPANDED){// 展开态}elseif(statusdisplay.FoldStatus.HALF_FOLDED){// 悬停态可以显示特殊布局}})除此之外适配时也要考虑下面的细节点不要完全依赖一套布局官网自己也说了「小屏幕场景如手表以及屏幕比例特殊的场景如 PuraX 外屏由于应用设计布局差异显著无法通过一套布局调整实现适配。」该写两套就写两套products 目录下分目录放用部署模型 B。天气案例里大屏用侧边栏切换城市小屏用 Swiper 滑动切换。这不是「样式」问题是「交互」问题。你得在代码里判断断点然后渲染不同的组件if(this.curBplg){// 大屏显示侧边栏this.showSideBartrue;}else{// 小屏Swiper 导航点this.showSideBarfalse;Swiper().indicator(newDotIndicator())}同时官方文档提醒aboutToAppear阶段组件刚创建窗口可能还没可见此时获取窗口尺寸可能不准。建议在onPageShow里获取。在 PC 上用户可能期望应用以小窗形式启动。如果你在onWindowStageCreate里调用了maximize窗口会强制全屏用户体验不好。总结前端开发者的一多学习路线官网文档里给了好几个业务场景的适配建议我快速过一遍方便你遇到类似需求时有个参考。影音娱乐类长视频、短视频、直播核心是沉浸式播放。大屏上可以显示更多互动内容评论、弹幕小屏上收起侧边栏。折叠屏悬停态时上半屏播放下半屏放控制栏。社交通讯类聊天、会议避免大屏上元素显示过大导致效率降低。宽屏设备可以用「列表 详情」的左右布局左边好友列表右边聊天内容。新闻阅读类首页在大屏上用多列布局延伸能力详情页用「边看边评」的左右布局左边文章右边评论区。电商购物类核心是展示更多商品。大屏上商品列表用多列网格商品详情页可以用侧边栏显示购物车。出行导航类手机用底部半模态面板平板和折叠屏用侧边半模态面板。面板支持多档位滑动调节充分利用大屏优势。这些案例的共同思路是先分析核心场景再根据设备屏幕特点选择合适的布局策略延伸、重复、挪移、分区等。而对于前端开发者大概一周左右就能上手布局适配。第一阶段1-2天搞明白断点和栅格学会matchMediaSync监听屏幕变化学会GridRow/GridCol的基本用法用order和columns的响应式配置第二阶段2-3天掌握常用容器组件SideBarContainer侧边栏Swiper轮播配合断点控制导航点显隐ScrolllayoutWeight解决内容溢出第三阶段1-2天理解工程架构common/features/products 三层怎么分HAR 包和 HAP 包的区别什么时候用部署模型 A什么时候用模型 B第四阶段实战拿天气案例练手Clone 官方 Demo跑起来改改断点阈值看看布局怎么变尝试加一个新模块用栅格塞进去最后说一句鸿蒙的「一多」不是银弹但它确实解决了很多前端开发者在多端适配上的重复劳动。尤其是栅格系统 断点监听这套组合用起来比手写媒体查询爽太多了。如果你用过 Element Plus 或者写过响应式页面上手这个案例基本没有门槛。官网的天气案例代码量不大但把核心概念都覆盖了值得花一个周末好好研究一下。有问题评论区聊。