把微信小程序迁移到鸿蒙元服务,如果选择 ASCF 框架,理论上只需要调整 API 调用,但实际操作后会发现框架、组件、接口、权限都存在差异。这篇文章从实战角度梳理迁移过程中遇到的典型问题,以及对应的处理方式,希望能给正在做类似迁移的开发者一些参考。
ASCF 框架的底层机制
ASCF 是 HarmonyOS 元服务的开发框架,运行时依赖 ASCF Runtime 加载编译后的资源文件。和微信小程序的最大区别在于全局对象:小程序使用 wx,ASCF 使用 has(harmony atomic service)。文件后缀也不一样,但语法基本保持一致,迁移时批量替换后缀即可,一般不会出现语法兼容问题。
用 ASCF Converter 做批量转换
DevEco Studio 内置了 ASCF Converter 转换工具,菜单路径是 Tools > ASCF Devtools > ASCF Converter。打开后选择小程序源码目录和输出目录,工具会自动完成文件后缀替换、wx 到 has 的批量改写。
使用时有几个需要注意的点。转换前要确保 ascf 目录为空,否则可能覆盖已有代码。如果小程序源码经过压缩混淆,建议加上 --notaddtodo 参数,否则转换器会在混淆代码里插入大量 TODO 注释,反而把代码搞乱。
转换完成后会生成两个文件:ascf/ascf_src/transform.log 和 ascf/ascf_src/_ascfConvertReport/index.html。transform.log 里记录了不支持的接口位置,转换报告可以在浏览器中打开,列出哪些接口和组件被成功转换、哪些暂不支持。实际迁移时建议对着这份报告逐个处理,比对着官方文档排查效率高很多。
Converter 能处理的只是机械性替换,框架差异和业务逻辑仍然需要手动调整。以下几个问题在转换后经常出现。
let 作用域差异
小程序里 let 声明的变量在某些场景下跨块可访问,但 ASCF 对块级作用域检查更严格,超出块作用域就访问不到。IDE 会直接提示这类错误,跟随提示修改声明位置即可。
0.5px 导致边框渲染异常
ASCF 要求 px 值必须是整型,写 0.5px 会导致显示效果异常。实际遇到的情况是渐变边框在开发者工具里显示正常,但真机上边框直接消失,排查很久才发现是 0.5px 的问题,改成 1px 后恢复正常。
toast 组件必须换成 API 调用
小程序模板里可以直接使用 <toast> 组件,但 ASCF 不支持这种写法,需要改成 has.showToast 调用。Converter 不会自动处理这个差异,因为组件名和 API 差异较大,只能手动替换。弹窗一直不出现的场景,优先检查是不是用了 <toast> 组件。
开源库需要手动复制源码
如果项目依赖了 vant-weapp 这类开源库,Converter 不会自动处理 node_modules 里的代码。需要把开源库的小程序源码手动拷贝到源码目录下,让转换器能扫描到这部分代码,具体操作可参考官方文档中“使用 npm 包”的说明。
三方框架编译 ASCF 的方式
除了直接用 Converter 转换,Uniapp 和 Taro 项目也可以直接编译成 ASCF 元服务。
Uniapp 从 HarmonyOS 元服务支持开始,可以使用 MP-HARMONY 条件编译隔离元服务和其他平台的代码,编译产物拷贝到 ascf/ascf_src 目录即可运行。
- // MP-HARMONY 条件编译示例
- // #ifdef MP-HARMONY
- console.info('这段代码只在元服务 ASCF 上跑');
- // #endif
- // #ifdef WEB
- console.info('这段代码只在 WEB 上跑');
- // #endif
复制代码
Taro 从 4.1.5 版本开始支持 ASCF,通过 process.env.TARO_ENV === 'ascf' 做条件编译。
- if (process.env.TARO_ENV === 'ascf') {
- require('path/to/ascf/name')
- } else if (process.env.TARO_ENV === 'h5') {
- require('path/to/h5/name')
- }
复制代码
这两种方式的优势是后续只需要维护一份代码框架,ASCF 工程通过工具输出。但转换完成后仍然需要做平台功能适配,比如接入华为账号、隐私托管等。
分包大小与 tabbar 页面限制
ASCF 支持分包,但限制和微信小程序不一样。单个包不超过 2MB,总包不超过 10MB,可以申请更大额度。
更需要留意的是 tabbar 页面必须在主包中。小程序里 tabbar 页面可以放在分包,但 ASCF 不允许,迁移时如果把 tabbar 页面放到分包,运行时会直接白屏。
平板适配问题
元服务在平板上默认居中显示,且无法设置强制竖屏,这一点和小程序不同。如果元服务只设计了竖屏布局,在平板上会出现显示异常。目前华为有规划优化这个问题,但现阶段需要开发者自己做响应式适配。
hjs 中 constructor 返回函数
hjs(内联脚本)中,数组的 constructor 返回值不是字符串,而是一个函数。
- // 小程序里
- let arr = [1, 2, 3];
- console.log(arr.constructor); // 输出: "Array"
- // ASCF 里
- let arr = [1, 2, 3];
- console.log(arr.constructor); // 输出: function Array() { ... }
复制代码
如果代码里用 constructor 做类型判断,会永远返回 false。需要改成 typeof 或 Array.isArray()。
导航栏标题白色不生效
API version 12 时,globalStyle 里设置 "navigationBarTextStyle": "white" 实际显示黑色,因为系统导航栏不支持设置为白色。解决方案是在 app.json 里把 navigationStyle 改成 custom,使用自定义导航栏。
- {
- "window": {
- "navigationStyle": "custom"
- }
- }
复制代码
改完后自己实现导航栏,颜色样式可以自由控制。
自定义组件不支持 getPageId
小程序里自定义组件可以通过 getPageId() 获取页面 ID,ASCF 暂时不支持这个方法。可以用在组件 properties 里传入页面 ID 的方式替代。
接口层面的兼容性差异
has.getClipboardData 在 ASCF 中暂不支持,系统对剪贴板权限管控较严格。如果元服务需要读取剪贴板内容(比如自动填充验证码),目前无法实现,只能等后续版本支持。
has.getAccountInfoSync 获取不到 appId 时,可以从 AppScope/app.json5 的 bundleName 里获取。
- // 替代方案
- const appConfig = require('../../AppScope/app.json5');
- const appId = appConfig.app.bundleName;
复制代码
权限差异是最容易踩坑的地方。
addPhoneContact 不需要授权
小程序里调用 addPhoneContact 需要先申请 scope.addPhoneContact 权限,但 ASCF 中直接调用即可。如果按小程序的习惯先调用 has.authorize,反而会报错说 scope 不存在。
- // 小程序里需要这样
- wx.authorize({
- scope: 'scope.addPhoneContact',
- success() {
- wx.addPhoneContact({ ... });
- }
- });
- // ASCF 里直接调用就行
- has.addPhoneContact({
- firstName: '张三',
- mobilePhoneNumber: '13800138000',
- success() {
- console.info('添加成功');
- }
- });
复制代码
保存到相册不需要授权
has.saveImageToPhotosAlbum 和 has.saveVideoToPhotosAlbum 也不需要 scope.writePhotosAlbum 权限,直接调用即可。
- // 直接调用,不需要授权
- has.saveImageToPhotosAlbum({
- filePath: '/tmp/test.png',
- success() {
- console.info('保存成功');
- }
- });
复制代码
迁移检查清单
从微信小程序迁移到 ASCF,整体改动量不大,核心是文件后缀替换和权限差异。最容易踩坑的是权限部分,有的接口在小程序里需要授权,在 ASCF 里反而不需要。
转换阶段:用 ASCF Converter 批量转换文件后缀和 wx 到 has;压缩源码加 --notaddtodo 避免 TODO 干扰;转换前确认 ascf 目录为空;转换后查看 transform.log 和 _ascfConvertReport 报告定位问题。
手动调整阶段:检查 let 作用域报错;非整型 px(如 0.5px)改为整型;<toast> 组件改为 has.showToast 调用;开源库源码手动拷贝到项目目录;Uniapp/Taro 项目考虑用条件编译直接输出 ASCF。
文件层面:.wxml 改成 .hxml,.wxss 改成 .css,.wxs 改成 .hjs,wx. 改成 has.。
框架层面:检查 tabbar 页面是否在主包;检查分包大小是否超限;检查 getPageId() 调用;检查 constructor 类型判断。
接口层面:getClipboardData 暂不支持需要替代方案;getAccountInfoSync 用 bundleName 替代;废弃接口不支持需要替换。
权限层面:addPhoneContact 不需要授权;saveImageToPhotosAlbum 不需要授权;saveVideoToPhotosAlbum 不需要授权;其他权限保持不变。
如果遇到 ASCF 还没支持的接口,可以发邮件到 atomicservice@huawei.com 反馈,邮件标题格式为 [ASCF框架接口诉求]-[元服务名称]-[APP ID]-[Developer ID]。实际反馈过一次,三个工作日就收到了回复。 |