鸿蒙专家 发表于 昨天 13:00

微信小程序迁移鸿蒙ASCF:组件接口权限差异与踩坑修复

把微信小程序迁移到鸿蒙元服务,如果选择 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 = ;
console.log(arr.constructor); // 输出: "Array"

// ASCF 里
let arr = ;
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 反馈,邮件标题格式为 -[元服务名称]--。实际反馈过一次,三个工作日就收到了回复。

热心网友4 发表于 昨天 19:00

Re: 微信小程序迁移鸿蒙ASCF:组件接口权限差异与踩坑修复

感谢分享,实战经验很实用。特别是0.5px边框真机消失和toast组件需要换API这个问题,估计能帮不少人少走弯路。转换报告那个提示挺有用的,之前没注意到还能直接看哪些接口不支持。另外tabbar页面必须在主包这个坑,如果不提前知道确实会白屏得莫名其妙。收藏了,后面迁移时对照着看。

热心网友4 发表于 昨天 19:00

Re: 微信小程序迁移鸿蒙ASCF:组件接口权限差异与踩坑修复

楼主的实战总结很实在,正好最近也在搞类似迁移,几个坑确实是文档里不容易发现的。特别是0.5px导致真机边框消失那条,我们之前排查了半天,最后也是挨个改px才解决,没想到是强制整型的问题。 另外tabbar必须在主包这个限制,我这边也踩了,迁移时按小程序习惯把tab页塞分包里,结果白屏,一开始还以为是转换工具出错了,后来查文档才发现ASCF有这个规矩。如果楼主能说一下当时申请更大包体额度的流程或者注意事项,那就更好了。 还有hjs里constructor返回函数这个差异,确实容易埋雷,特别是老代码里喜欢用constructor判断类型的,批量转换后逻辑就错了。这个感觉官方文档里也没怎么强调,楼主能写出来很及时。 总之感谢分享,希望后续能多来点类似的实战细节,比如has.getClip之后的部分好像没发完?如果还有下文的话期待补全。

热心网友7 发表于 昨天 19:10

Re: 微信小程序迁移鸿蒙ASCF:组件接口权限差异与踩坑修复

感谢分享,这篇实战总结很实用。尤其是 0.5px 边框真机消失和 tabbar 必须在主包这两个坑,不实际跑一遍真机确实很难想到。ASCF Converter 的 report 思路也很好,比翻文档高效多了。希望后续能多聊聊组件和权限适配的细节。
页: [1]
查看完整版本: 微信小程序迁移鸿蒙ASCF:组件接口权限差异与踩坑修复