如果你刚开始搞鸿蒙跨平台开发,第一道坎往往不是代码本身编译不过,而是开发环境怎么配都报错。我自己在 Windows 上折腾 KuiklyUI 这套跨平台 UI 框架时,光环境变量就反复配了好几轮,后来又发现编译脚本里的调用顺序、缓存清理、SDK 版本对应关系全是坑。这篇文章就把我在 Windows 上从零配置开发环境、写编译脚本、最终跑通完整编译链路的过程记录下来。适合刚接触鸿蒙跨平台开发、熟悉 Windows 但不想折腾 Linux 双系统的开发者参考,也适合已经有项目基础、想用脚本把重复操作固化下来的朋友直接抄作业。
1. 环境配置前,先搞懂整体思路
1.1 KuiklyUI 到底是什么,为什么能在 Windows 上开发
KuiklyUI 这类跨平台 UI 框架,它的核心思路是:你用一套统一的声明式 UI 代码写界面,框架在编译期把 UI 描述映射到不同平台的原生组件上。对于鸿蒙这个目标平台,最终产物不是网页、不是 JS 解释执行,而是鸿蒙应用工程里的原生 UI 组件。也就是说,你在 Windows 上写完代码,按一下脚本,得到的可以直接是鸿蒙的安装包,而不是中间还要套一层浏览器容器。
理解这一点很重要,因为很多人会下意识把“跨平台”等同于“用网页技术包一层壳”。而 KuiklyUI 这类框架走的路径更加接近原生,它在编译阶段就需要调用鸿蒙提供的编译工具链,就绕不开 SDK、命令行工具、依赖管理工具这几样东西。这也是为什么环境配置会比普通前端项目麻烦一些。
在 Windows 上开发鸿蒙应用,技术上完全可行。鸿蒙官方的 IDE 和命令行工具都提供了 Windows 版本,编译 HAP 包、连接设备、打印日志这些操作,在 Windows 上都有对应工具。你不需要准备一台 mac,也不需要装 Linux 虚拟机。唯一要注意的是,工具链版本之间会有绑定关系,比如 IDE 版本、SDK 版本、依赖管理工具版本,必须匹配,否则编译到一半会冒出各种奇怪的错误。
1.2 Windows 在整个开发链路里扮演什么角色
我们先把整条开发链路拆开看:
- 代码编辑:在 Windows 上用 IDE 或编辑器写跨平台 UI 代码,包括页面、组件、状态管理。
- 依赖安装:通过包管理工具安装 JS 依赖和鸿蒙相关依赖。
- 编译打包:调用鸿蒙构建工具,把 UI 代码编译成鸿蒙的构建产物。
- 安装调试:通过命令行工具把产物装到模拟器或真机上,查看 UI 效果。
在这条链路里,Windows 是开发主机,也是编译主机。只要你把环境变量、SDK 路径、依赖管理工具配对,后面所有操作都可以在命令行里完成。我自己习惯的做法是,把 IDE 里能点到按钮的操作,尽量用脚本替代,因为项目一多,或者要连续出包验证时,脚本的效率远远高于反复打开 IDE 点按钮。
1.3 先认清一个误区:环境变量不是配一次就完事
我踩过的第一个大坑,就是以为环境变量配置好之后,任何终端里都能正常编译。实际上,很多工具在安装时会往用户环境变量里写内容,但是命令行工具在编译时,读取的可能还有项目级配置、用户目录下的全局配置文件,以及 IDE 替你维护的那一套内部配置。
比如,你手动配了 JAVA_HOME,但 IDE 自带的终端可能加载的是它自己认的那套 JDK;你在命令行里安装了依赖,但 IDE 里打开项目时它又可能重新解析一遍。这种“环境变量看起来配好了,但实际编译时用的不是同一个环境”的问题,非常隐蔽。所以我把整个环境配置的思路定成了三个原则:
- 固定目录,避免默认路径里带中文和空格。
- 能用项目内引用的,就不要挂在全局 PATH 上。
- 尽量写一个统一的环境加载脚本,让命令行和脚本共用同一套变量。
2. 开发环境配置全流程
2.1 开始之前:先把目录结构固定下来
我建议你先建一个专门的工作目录,比如在 D 盘下建一个kuikly-dev,下面分tools、workspace、output三个子目录:
tools:放 IDE 安装目录、SDK、命令行工具。workspace:放你的项目代码仓库。output:放编译产物。
为什么不建议直接装在默认的用户目录?因为 Windows 的用户名很多时候是中文,或者带空格,虽然现代工具大部分能处理,但一旦遇到某个脚本没给路径加引号,编译就会挂得莫名其妙。另外,有些工具链对路径里的特殊字符很敏感。把这些工具统一放到一个没有特殊字符的路径下,能避免很多间歇性报错。
2.2 安装基础运行时:Node.js 和 JDK
第一步先把 Node.js 装上。KuiklyUI 这类跨平台框架,通常都有 JS/TS 生态,编译脚本和依赖管理都会用到 Node.js。我装的是 LTS 版本,也就是当前推荐给大多数用户的稳定版。装上之后在命令行里跑一下:
node -v npm -v能正常输出版本号就说明 Node 环境没问题。
然后是 JDK。鸿蒙工程的构建工具链是跑在 Java 虚拟机上的,所以必须要有 JDK。这里要注意,不是随便装一个最新版就完事,工具链可能只认某个范围内的版本。我使用的是 JDK 17,如果你的 SDK 版本比较旧,也可能需要 JDK 11。具体以你安装的 IDE 或 SDK 的说明文档为准。
装完 JDK 之后,要把JAVA_HOME配到系统环境变量里。这一步不要省略,因为很多命令行工具在启动时都会去读这个变量。配置方法很简单:右键“此电脑” -> 属性 -> 高级系统设置 -> 环境变量,新建一个JAVA_HOME,值填你的 JDK 安装路径,然后在Path里加上%JAVA_HOME%\bin。
2.3 安装鸿蒙 IDE 和鸿蒙 SDK
接下来安装鸿蒙开发的官方 IDE。安装包可以在鸿蒙开发者官网下载,安装过程基本是下一步下一步。第一次启动后,它会引导你安装 SDK 组件。
这里我给一个关键建议:安装 SDK 时,把安装路径手动改到你前面建的D:\kuikly-dev\tools目录下面,不要用默认路径。因为默认路径往往是一长串带空格的路径,看着就让人不放心。SDK 安装完成后,你需要在 IDE 的设置里确认 SDK 路径,最好顺便把ohpm、hdc这些命令行工具所在的目录记下来。
ohpm是鸿蒙生态的包管理工具,负责安装鸿蒙依赖,类似前端里的npm;hdc是设备调试工具,负责把编译产物安装到设备或模拟器上。这两个工具通常在 SDK 的toolchains或command-line-tools目录下。我建议把它们的路径加到系统Path环境变量里,这样你在任何目录下运行ohpm -v、hdc -v都能直接识别。
2.4 验证环境:一条条检查,别偷懒
环境装完之后,打开一个全新的命令行窗口,依次执行:
node -v npm -v java -version ohpm -v hdc -v如果哪一条提示“不是内部或外部命令”,说明对应的环境变量没配好。这里要注意,修改完环境变量后,已经打开的终端不会自动生效,一定要新开一个终端再试。
另外,你可以再检查一下 SDK 路径是否被 IDE 正确识别。新建一个空的鸿蒙项目,看它能不能正常同步依赖。这一步能从侧面确认 IDE 自带的构建环境和你命令行里的环境是不是同一个版本。如果 IDE 能编译,但命令行不能,大概率是环境变量或终端配置的问题。
2.5 环境变量的正确配置方式
很多教程喜欢让你手动往系统环境变量里加一堆东西,但我不推荐这样。手动维护的变量一旦多了,不同项目之间很容易冲突。我更推荐的方式是:
- 系统环境变量只保留三个:
JAVA_HOME、Path里指向 Node 和ohpm工具链的目录、以及 SDK 根目录的DEVECO_SDK_HOME。 - 其他路径,能放在项目脚本里动态拼接的,就不要挂到全局。
这样做的原因是,你可能会在一个电脑上同时维护多个项目,而不同项目可能依赖不同版本的 SDK 或工具链。全局变量只保留最通用、最稳定的内容,项目相关的路径交给脚本自己拼,切换项目时互不影响。
3. 编译脚本设计与实现
3.1 一次完整编译到底包含哪些环节
在写脚本之前,先要清楚一次编译会经历哪几步。以我实践中遇到的情况,大致是这样:
- 安装/更新依赖:
npm install负责拉取 JS 生态依赖,ohpm install负责拉取鸿蒙生态依赖。 - 解析工程配置:构建工具读取模块配置、SDK 版本、启动页面等,生成编译计划。
- 编译 UI 代码:把跨平台代码转成鸿蒙运行时的中间表示或目标代码。
- 资源打包:把图片、布局、配置等打包进去,最终生成 HAP 文件。
这四个环节不是孤立的,任何一个环节失败,后续都走不下去。所以脚本的第一个作用就是保证顺序正确。很多人直接在命令行里手动敲命令,漏掉某一步,最后编译报错,还以为源码有问题。脚本化之后,这种顺序性错误就很少发生了。
3.2 脚本参数和错误处理设计
我写的编译脚本一定包含这三个参数:
Target:指定编译目标,比如harmony,后续要加入其他端时只需要扩展这个枚举。Configuration:指定是调试包还是发布包,Debug和Release对应不同的签名和优化级别。SkipInstall:布尔参数,如果依赖已经安装,就跳过安装步骤,加快编译速度。
参数设计上有个原则:默认值要能直接跑。也就是说,你双击脚本或直接运行build.ps1,它应该用一套合理的默认值帮你把整个流程走完。参数只是为了覆盖特殊场景,而不是让你每次都要敲一长串。
错误处理也很重要。PowerShell 里我建议第一行就写上:
$ErrorActionPreference = "Stop"它的作用是:任何命令返回错误,脚本立即退出,而不是继续往下执行。你肯定不希望依赖装到一半,编译就带着残缺环境往下跑,最后产出一个不可用的包,排查起来特别费劲。
3.3 一个可复用的 build.ps1 脚本
这里给一个我在 Windows 上实际用过的脚本骨架,路径和命令名称统一用通用的写法,你按自己的目录稍作修改即可。
param( [string]$Target = "harmony", [string]$Configuration = "Debug", [switch]$SkipInstall ) $ErrorActionPreference = "Stop" # 固定好项目根目录和产物目录 $ProjectRoot = Split-Path $PSScriptRoot -Parent $OutputDir = Join-Path $ProjectRoot "output" $DevEcoSdkHome = "D:\kuikly-dev\tools\sdk" $env:DEVECO_SDK_HOME = $DevEcoSdkHome $env:JAVA_HOME = "D:\kuikly-dev\tools\jdk" if (-not $SkipInstall) { Write-Host "=== [1/4] 安装 npm 依赖 ===" Push-Location (Join-Path $ProjectRoot "frontend") npm install --registry=https://registry.npmmirror.com Pop-Location Write-Host "=== [2/4] 安装鸿蒙依赖 ===" Push-Location (Join-Path $ProjectRoot "harmony") ohpm install Pop-Location } Write-Host "=== [3/4] 开始编译 目标=$Target 配置=$Configuration ===" Push-Location (Join-Path $ProjectRoot "harmony") hvigorw assembleHap --mode module -p product=$Configuration $BuildExitCode = $LASTEXITCODE Pop-Location if ($BuildExitCode -ne 0) { Write-Host "编译失败,退出码=$BuildExitCode" exit $BuildExitCode } Write-Host "=== [4/4] 收集产物 ===" if (-not (Test-Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir | Out-Null } Copy-Item -Path (Join-Path $ProjectRoot "harmony\build\**\*.hap") -Destination $OutputDir -Force Write-Host "编译完成,产物目录:$OutputDir"这段脚本做了三件事:加载项目所需的环境变量,按顺序安装依赖并编译,最后把 HAP 产物统一复制到output目录。你可以看到,我没有在系统全局变量里配置 SDK 路径,而是在脚本里通过$env:DEVECO_SDK_HOME临时指定,这样换机器、换项目都不影响全局环境。
3.4 附加脚本:清理和更新依赖
编译项目时间久了,你会遇到一种情况:改了依赖版本,但构建工具还在用旧缓存,导致编译结果不对。所以我额外写了一个clean.ps1:
$ErrorActionPreference = "Stop" $ProjectRoot = Split-Path $PSScriptRoot -Parent $HarmonyDir = Join-Path $ProjectRoot "harmony" Push-Location $HarmonyDir if (Test-Path "build") { Remove-Item -Recurse -Force "build" } if (Test-Path "node_modules") { Remove-Item -Recurse -Force "node_modules" } if (Test-Path "oh_modules") { Remove-Item -Recurse -Force "oh_modules" } Pop-Location Write-Host "清理完成"清理脚本的核心就是把build、node_modules、oh_modules这几个最容易出问题的缓存目录删掉。注意,这个操作会把整个依赖目录删干净,下次编译时需要重新安装所有依赖,所以一般只在怀疑依赖污染时使用。另外一个update_deps.ps1就简单了,它的任务只是依次执行npm install和ohpm install,不给编译步骤,专门用来刷新依赖。
3.5 把脚本接入 IDE 外部工具或 CI
脚本写好后,你不一定要每次打开命令行手动运行。我通常会在 IDE 的外部工具里配置一个“一键编译”入口,把参数填成默认值,这样按一个快捷键就能触发编译流程。
如果你有 CI 需求,脚本也能直接接入流水线。CI 里调用脚本时,注意几点:
- 流水线机器上也要提前配好 Node、JDK、SDK。
- 不要依赖有人手动点 IDE 去初始化项目,而是让 CI 自动执行脚本里的安装步骤。
- 把
output目录设置为流水线的产物目录,方便后续打包上传。
我见过不少项目,本地编译没问题,一到 CI 就挂,原因就是 CI 机器上的环境变量和本地不完全一致。脚本里显式加载所有路径,能最大程度避免这种“本地能跑、线上不能跑”的问题。
4. 实操记录:从克隆到安装到模拟器
4.1 克隆代码与第一次依赖安装
实际动手时,我先在D:\kuikly-dev\workspace下克隆了一个模拟项目的仓库。假如你拿到的项目地址在某代码托管平台上,命令大概是:
git clone https://example.com/your-repo.git cd your-repo克隆完成后,我先检查了项目根目录下的说明文档,确认它依赖的 Node、JDK、SDK 版本。这一步不要跳过,因为不同分支可能用的 SDK 版本不一样,盲目开搞很容易踩版本坑。
接着我运行了更新依赖的脚本:
powershell -ExecutionPolicy Bypass -File .\scripts\update_deps.ps1-ExecutionPolicy Bypass这个参数很有用。Windows 默认的 PowerShell 执行策略会阻止脚本运行,加上它相当于临时放行,不用去改系统策略。
依赖安装过程中,我遇到过一次ohpm install卡住不动的情况。排查后发现是网络原因,后来把ohpm的 registry 配置成国内镜像,问题就解决了。至于怎么配,命令一般类似:
ohpm config set registry https://repo.harmonyos.com/repository/ohpm/具体地址以官方文档为准。配置完之后,再跑一次依赖安装就顺畅多了。
4.2 跑编译脚本与日志解读
依赖装好之后,我直接运行:
powershell -ExecutionPolicy Bypass -File .\scripts\build.ps1 -Configuration Debug第一次编译日志会输出很多信息,但你只需要关注几个关键节点:
- 依赖解析阶段:看日志里有没有
ERROR或FAILURE。 - 模块编译阶段:看有没有提示找不到符号、找不到 Java 类。
- 打包阶段:看有没有提示 HAP 文件路径。
这里我总结了一个经验:编译日志里出现的WARNING大部分可以先忽略,但ERROR一定要看到底。有些错误提示很模糊,比如“Process failed with exit code 2”,这时候光看日志末尾不够,要把出错位置前面几十行的上下文翻开,看是哪一步调用的命令出了问题。
4.3 出包路径与产物验证
编译成功后,我的脚本会自动把 HAP 文件复制到output目录。你可以在输出目录里看到类似这样的文件:
output\entry-default-signed.hap得到 HAP 之后,先别急着往真机上装。你可以先用 IDE 自带的预览器打开工程,检查 UI 在模拟环境下的表现。这一步能快速发现页面布局、状态管理的问题,比反复装真机要快很多。
如果要用模拟器,就通过 IDE 启动模拟器,然后使用命令行工具安装:
hdc list targets hdc install output\entry-default-signed.haphdc list targets的作用是确认设备有没有被识别,如果输出为空,说明设备没连上或驱动有问题。
4.4 真机调试和日志查看
真机调试和模拟器类似,只是需要先用数据线把设备连到电脑上,并在设备上开启开发者模式。连上后,你同样可以用hdc list targets确认设备在线。
查看应用日志是另一项高频操作。我通常用:
hdc hilog把应用跑起来后,UI 渲染和业务逻辑的日志都会刷出来。如果你在 UI 层写了console.log或者框架自带的日志 API,这里都能看到。真机调试时我最建议新手先跑通一个最简单的页面,再逐步加业务逻辑。因为同一个构建工具链,前后端都能跑通之后,调试复杂问题时你才有信心判断是环境问题还是代码问题。
5. 常见问题排查方法与避坑技巧
5.1 脚本无法执行,提示策略限制
Windows 上第一次跑.ps1脚本时,经常会遇到“因为在此系统上禁止运行脚本”的提示。这是因为默认执行策略是受限的。我的做法是,执行脚本时都加上-ExecutionPolicy Bypass,而不是全局修改执行策略。
如果懒得每次加参数,也可以在当前用户下执行一次:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned这个命令只影响当前用户,不影响整个系统,相对安全。但如果你在公司电脑上,可能不方便权限设置,那还是用-ExecutionPolicy Bypass更稳妥。
5.2 依赖安装失败,超时或找不到包
ohpm install超时,大概率是网络源的问题。配置镜像源,或者把你的网络环境切换到更适合国内访问的通道,基本能解决。如果提示“找不到某个包”,先确认一下你的目标仓库和ohpm镜像源是不是同步了。有时候本地缓存了旧版本的包元数据,需要执行缓存清理:
ohpm cache clean再重新ohpm install。
npm 依赖安装失败,则看报错定位是权限、网络还是版本冲突。如果是在公司内网,需要走代理的话,先确认 npm 的代理配置:
npm config get proxy npm config get https-proxy只要有值,说明 npm 在走代理,代理不通就会安装失败。把这些配置设为空,再重新安装即可。
5.3 SDK 版本不一致导致的奇怪报错
这个坑最隐蔽。你安装的 IDE 和 SDK 如果是最新版,但项目是在旧版本 SDK 下创建的,编译时会出现各种莫名其妙的错误,比如某个 API 找不着,某个资源格式不被识别。报错可能指向源码,但根源并不在源码。
我的排查方法是:
- 查看项目构建配置文件里写的 compatibleSdkVersion 和 targetSdkVersion。
- 查看本机安装的 SDK 版本。
- 如果两者不匹配,优先把项目配置改成跟你本机 SDK 匹配的版本,或重新安装对应版本的 SDK。
这里要特别提醒,不要盲目把项目配置的 SDK 版本调高,因为有些旧代码用到了新版本里已经移除的接口,调高反而引发更多错误。最好的方式是在团队内部统一一个版本。
5.4 缓存污染问题
当你明明改了代码,但是编译完的效果没变,或者出现了莫名其妙的构建错误,大概率是缓存污染。最常见的三个缓存位置:
- 项目下的
build目录。 - 项目下的
node_modules和oh_modules目录。 - 用户目录下全局的构建缓存目录。
处理方式很简单:先跑clean.ps1,再重新执行完整构建。如果仍然有问题,再手动删掉全局缓存目录。很多构建工具有自己的缓存 key 设计,项目路径改一下、环境变量变一下,可能就会重新全量编译,这是正常现象,不用太紧张。
5.5 常用问题排查速查表
我在实际过程中整理过一份速查表,遇到问题先对照查一遍,能省大量时间。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 终端提示找不到命令 | 环境变量没生效 | 新开终端,或补全环境变量 |
| 编译中断,报错信息里有乱码 | 工程路径包含中文或空格 | 把工程移到纯英文、无空格路径 |
| 依赖安装一直卡住 | 网络源问题 | 配置国内镜像源,清理缓存 |
| 编译到一半提示 Java 版本不对 | JDK 版本与 SDK 不匹配 | 确认 JDK 要求,切换对应版本 |
| 改了代码,产物没变化 | 构建缓存未刷新 | 执行 clean 脚本后重编 |
| HAP 安装到设备失败 | 设备驱动或签名问题 | 检查 hdc 是否识别设备,确认签名配置 |
这张表看起来简单,但每一条我都实打实踩过。尤其是“工程路径包含中文”这一条,新手很少意识到,等到报错报得莫名其妙,再回头看路径,才发现问题一早就埋下了。
5.6 提升编译速度的三个小优化
编译一次动辄好几分钟,如果每天编译几十次,时间成本很可观。我有三个小优化,实测下来效果不错。
第一个,是尽量使用增量构建。不要每次都在脚本里强制clean。只有依赖变了、SDK 版本变了、或者出现了不明问题,才需要全量清理重置。日常开发时,我直接用build.ps1 -SkipInstall,跳过依赖安装,用增量编译。
第二个,是把 Node.js 和 ohpm 的缓存目录固定到一个 SSD 盘。如果你默认缓存放在机械硬盘或系统盘,读写速度都会拖慢整体构建。可以通过配置命令把缓存目录改到D:\kuikly-dev\cache下。
第三个,是在 Windows 安全中心里,把工作目录添加为排除项。杀毒软件实时扫描构建目录时,会频繁读取临时文件,导致编译明显变慢。排除掉扫描后,编译速度提升非常明显。公司电脑上如果权限受限做不了,至少可以只排除output目录,避免每次拷贝产物时被杀毒软件拦截。
6. 最后再说几句掏心窝的话
我个人练了几次之后,最大的体会是:环境配置没有想象中难,但也没有教程里写的那么“一路下一步就行”。真正花时间的,不是安装过程,而是把各种隐藏条件对齐。比如 SDK 版本、依赖目录、命令行工具的位置、PowerShell 执行策略,这些细节任何一个不对,编译就会给你脸色看。
如果你刚开始接触这套流程,我建议你先不要急着写什么一键脚本。老老实实把 IDE 里的编译按钮跑通,再手动跑一遍命令行命令,等你知道每一步为什么会成功、为什么会失败之后,再去写脚本。否则脚本只会把一堆报错封装起来,让你更看不懂问题出在哪。
脚本化绝对是值得做的事。它不仅能让你省下点按钮的重复劳动,还能让电脑在无人值守时自动出包,方便后续交给 CI 流程。但前提是,你得先理解它背后的编译链路。环境配置、依赖安装、编译打包、安装调试,这一整条流水线,在 Windows 上完全可以跑得很顺。最重要的就是固定目录、固定版本、用脚本统一环境,少让环境变量满天飞。这样下来,哪怕你中途隔几个月不碰,再打开电脑重新跑一次构建,也不会一脸懵。