uni-app x 在鸿蒙平台可以用 Worker 把耗时任务移出主线程。原文给出的版本边界是:鸿蒙平台从 Worker 4.81 开始支持,Web 4.81 也支持,微信小程序 4.41 支持,iOS 目前不支持。Worker 是真正的多线程,原文特别提醒它与 Android request API 内部使用协程不是一回事。主线程负责 UI 渲染,大量计算留在主线程会卡界面,这是使用 Worker 的直接场景。
一、创建 Worker 与通信模型
API 入口是 uni.createWorker(url: string),传入 Worker 脚本 URL,返回 Worker 对象。主线程侧常用 postMessage(message, options?)、onMessage(callback)、onError(callback) 和 terminate()。其中 terminate() 仅限在主线程调用。Worker 脚本是独立的 .uts 文件,脚本里通过 self 对象与主线程通信:self.onMessage 接收消息,self.postMessage 返回结果,self.env.USER_DATA_PATH 可读取用户数据路径。基础示例如下:
- // workers/helloWorkerTask.uts
- self.onMessage((message: any) => {
- const data = message as UTSJSONObject
- const value = data['data'] as string
- const num = parseInt(value) || 0
- self.postMessage({ data: (num + 1).toString() })
- })
复制代码
主线程创建、监听和发送消息:
- const worker = ref<Worker | null>(null)
- worker.value = uni.createWorker('workers/helloWorkerTask.uts')
- worker.value!.onMessage((message: any) => {
- const data = message as UTSJSONObject
- result.value = (data['data'] as string) || ''
- })
- worker.value!.onError((error) => {
- console.error('Worker 错误:', error.errCode, error.errMsg)
- })
- worker.value!.postMessage({ data: inputValue.value, needReply: true })
- // 页面卸载时
- worker.value!.terminate()
复制代码
这里要守住两条边界:UI 操作必须在主线程,子线程不能操作 DOM API,也不能操作绑定在界面上的响应式变量;共享变量需要线程安全,避免多个线程同时写同一个变量,或一个线程读、另一个线程在写。
二、耗时计算、JSON 处理与并行
大量计算是最典型的落点。把循环放到 Worker 脚本里,主线程只负责创建、发送参数和接收结果:
- self.onMessage((message: any) => {
- const data = message as UTSJSONObject
- const count = (data['count'] as number) || 1000000
- let sum = 0
- for (let i = 1; i <= count; i++) {
- sum += i
- }
- self.postMessage({ result: sum, count: count })
- })
复制代码
主线程在 onMessage 里读取 result,拿到结果后调用 terminate() 结束该 Worker。JSON 数据处理同理:Worker 接收 JSON 字符串,解析后按 status 过滤 active 项,再通过 self.postMessage 返回总数、过滤后数量和序列化数据。多个 Worker 并行时,可以用 completedCount 统计完成数量,全部完成后统一处理结果。但原文明确提醒不要开太多线程,CPU 核数有限,线程开太多反而影响性能。
三、鸿蒙 Sendable 共享变量
鸿蒙平台提供了 Sendable 协议,可通过 harmonySendable 选项实现共享变量。原文指出,这种方式的性能比普通 postMessage 好很多,适合在鸿蒙侧传递较大数据或共享变量的场景。调用时在 postMessage 的第二个参数中传入选项:
- worker.postMessage({ data: 'large data' }, { harmonySendable: true })
复制代码
使用共享变量时仍要回到线程安全:避免多线程同时写同一个变量,或一个线程读、另一个线程在写。Sendable 是性能手段,不是绕过线程安全约束的理由。
四、错误码排障与生命周期
Worker 运行时错误通过 onError 监听。原文给出的错误码包括:5000501 表示 Worker 运行错误,5000504 表示 Worker 线程中不支持调用的 API,5000506 表示 Worker 文件路径无效,5000505 表示 Worker 线程初始化失败。排障时先看 errCode 和 errMsg,再核对 Worker 脚本路径、是否在子线程调用了不支持的 API,以及 Worker 线程初始化条件。示例:
- worker.onError((error) => {
- if (error.errCode == 5000501) {
- console.log('Worker 运行错误')
- } else if (error.errCode == 5000504) {
- console.log('Worker 线程中不支持调用的 API')
- } else if (error.errCode == 5000506) {
- console.log('Worker 文件路径无效')
- } else if (error.errCode == 5000505) {
- console.log('Worker 线程初始化失败')
- }
- })
复制代码
生命周期上,页面卸载时要 terminate() 并清空 Worker 引用,创建前也要防重复创建。Worker 脚本内还可通过 self.env.USER_DATA_PATH 访问用户数据路径,用于文件相关操作。
五、实践取舍与验证
跨端项目要先看支持范围:iOS 目前不支持 Worker,原文没有给出替代实现,工程上需要按平台能力做评估。验证时重点覆盖基础通信、耗时计算、JSON 过滤、多 Worker 并行完成计数和错误码分支;观察主线程是否仍能响应、onMessage 是否拿到预期结果、onError 是否命中对应错误码、页面卸载后 Worker 是否已销毁。架构上不要把 Worker 当成越多越好,按任务拆分、按核数控制并发,耗时任务放子线程,UI 和响应式变量操作留在主线程。
原文的核心结论很明确:uni-app x 在鸿蒙上用 Worker 解决主线程卡顿,关键不是会调 createWorker,而是把通信、Sendable、错误码和销毁时机一起设计好。 |