页面跳转带数据,这谁不会?pushPath的时候把参数传过去就完了。但现实远没这么简单——传个字符串没问题,传个对象呢?传个Date呢?传个100KB的JSON呢?跨Ability跳转呢?返回时怎么带数据回来?每个问题踩一遍,你就知道"传参"这两个字背后有多少坑。

Navigation参数传递

在这里插入图片描述

NavPathStack的pushPath支持param参数:

this.navPathStack.pushPath({
  name: 'DetailPage',
  param: { id: 42, title: '测试文章' }
});

目标页面通过onGetParam回调接收:

@Builder
DetailPage() {
  NavDestination() {
    DetailContent()
  }
  .onGetParam((param: Object) => {
    let p = param as Record<string, Object>;
    this.itemId = p['id'] as number;
    this.itemTitle = p['title'] as string;
  })
}

关键:param的类型是Object,需要手动转换。 ArkTS不支持泛型参数,所以pushPath时传入的param在接收端是Object类型,必须用as转换或逐字段取值。

参数类型限制

Navigation的param参数只支持可序列化类型。这意味着:

类型 能传吗 说明
number/string/boolean 基本类型没问题
普通对象(Record) 字段都是基本类型
数组 元素是基本类型
Date 不能 反序列化后变成string
Map/Set 不能 序列化后丢失类型
函数/回调 不能 不可序列化
class实例 看情况 只有序列化友好的字段能保留

Date是高频踩坑点。你以为传了个Date对象过去,收到一打开发现是个ISO字符串。解决方式是传时间戳:

// 发送端
this.navPathStack.pushPath({
  name: 'SchedulePage',
  param: { timestamp: Date.now() }
});

// 接收端
let timestamp = p['timestamp'] as number;
let date = new Date(timestamp);

复杂对象传递

对象嵌套对象时,序列化会"展平"类型信息:

interface Address {
  city: string;
  street: string;
}

interface User {
  name: string;
  age: number;
  address: Address;
}

// 传参
let user: User = {
  name: '张三',
  age: 25,
  address: { city: '北京', street: '中关村' }
};
this.navPathStack.pushPath({ name: 'UserDetail', param: user });

// 接收端——address的类型信息丢失了
.onGetParam((param: Object) => {
  let p = param as Record<string, Object>;
  let name = p['name'] as string;
  let addressObj = p['address'] as Record<string, Object>;
  let city = addressObj['city'] as string;
})

嵌套层级越深,类型转换越痛苦。实际项目中建议只传ID,到目标页面再查数据

// 只传ID
this.navPathStack.pushPath({ name: 'UserDetail', param: { userId: user.id } });

// 目标页面用ID查完整数据
.onGetParam(async (param: Object) => {
  let p = param as Record<string, Object>;
  let userId = p['userId'] as number;
  this.user = await this.userService.getById(userId);
})

只传ID的好处:数据永远是新鲜的(不会因为传了旧数据而显示过期内容),参数体积小,不需要序列化复杂对象。

返回值传递

NavPathStack支持pop带返回值:

// 详情页返回时带数据
this.navPathStack.pop({ updated: true, itemId: 42 });

// 上一个页面接收
this.navPathStack.pushPath({
  name: 'DetailPage',
  param: { id: 42 }
}).then((result: Object) => {
  let r = result as Record<string, Object>;
  let updated = r['updated'] as boolean;
  if (updated) {
    this.refreshList();
  }
});

注意:不是所有pushPath都支持then接收返回值。 只有当目标页面用pop(Object)返回时,then才会触发。如果用户点系统返回键或者手势返回,pop没带参数,then就不会触发。

更可靠的方式是监听navPathStack的变化:

aboutToAppear(): void {
  this.navPathStack.on('pop', (info: PopInfo) => {
    if (info.param !== undefined) {
      let result = info.param as Record<string, Object>;
      let updated = result['updated'] as boolean;
      if (updated) {
        this.refreshList();
      }
    }
  });
}

on(‘pop’)在每次栈弹出时都会触发,不管是代码pop还是手势返回。比then更可靠。

大数据传递

参数体积过大(超过100KB)时,Navigation的序列化会有性能问题。几个解决方案:

  1. 全局状态:把数据放到AppStorage或PersistentStorage,只传key
// 写入全局状态
AppStorage.setOrCreate('draftArticle', article);

// 只传标记
this.navPathStack.pushPath({ name: 'Preview', param: { from: 'draft' } });

// 目标页读取
let article = AppStorage.get<Article>('draftArticle');
  1. 文件中转:数据写临时文件,传文件路径
import { fileIo } from '@kit.CoreFileKit';

// 写入临时文件
let tempPath = this.context.tempDir + '/transfer_data.json';
fileIo.writeTextSync(tempPath, JSON.stringify(largeData));
this.navPathStack.pushPath({ name: 'Target', param: { filePath: tempPath } });

// 目标页读取
let content = fileIo.readTextSync(filePath);
  1. 单例管理器:用内存中的单例持有数据
class DataTransferManager {
  private static instance: DataTransferManager;
  private cache: Map<string, Object> = new Map();

  static getInstance(): DataTransferManager {
    if (!DataTransferManager.instance) {
      DataTransferManager.instance = new DataTransferManager();
    }
    return DataTransferManager.instance;
  }

  put(key: string, data: Object): void {
    this.cache.set(key, data);
  }

  get(key: string): Object | undefined {
    return this.cache.get(key);
  }

  remove(key: string): void {
    this.cache.delete(key);
  }
}

// 使用
let transferKey = 'data_' + Date.now().toString();
DataTransferManager.getInstance().put(transferKey, largeData);
this.navPathStack.pushPath({ name: 'Target', param: { transferKey: transferKey } });

// 目标页
let data = DataTransferManager.getInstance().get(transferKey);
DataTransferManager.getInstance().remove(transferKey);

单例方式最简单,但要记得用完删除,否则内存泄漏。

跨Ability传递

跨Ability跳转用want传参:

import { common, Want } from '@kit.AbilityKit';

let want: Want = {
  bundleName: 'com.example.app',
  abilityName: 'SecondAbility',
  parameters: {
    userId: 42,
    userName: '张三'
  }
};
this.context.startAbility(want);

目标Ability在onCreate/onWindowStageCreate中获取参数:

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  let params = want.parameters;
  let userId = params['userId'] as number;
}

跨Ability的parameters限制更严格——只支持基本类型和简单对象,不支持函数、class实例、循环引用对象。这跟Navigation的param限制一样,但跨Ability序列化是系统级操作,成本更高。

参数校验

永远不要假设参数一定存在:

.onGetParam((param: Object) => {
  if (param === null || param === undefined) {
    // 无参数进入,显示默认内容
    this.itemId = -1;
    return;
  }
  let p = param as Record<string, Object>;
  if (p['id'] !== undefined) {
    this.itemId = p['id'] as number;
  } else {
    this.itemId = -1;
  }
})

用户可能从深链接、通知、桌面快捷方式进入,这些场景可能没有参数。不校验就crash。

参数与深链接

deepLink进入时参数通过want传递,格式是URI:

// scheme://host/path?key=value
let want: Want = {
  uri: 'myapp://detail?id=42&from=notification'
};

// 在Ability中解析
let uri = want.uri as string;
let url = new URL(uri);
let id = url.searchParams.get('id');

URL解析支持查询参数。但注意:URI里的参数都是字符串类型,"42"需要手动parseInt转数字。

踩坑清单

问题 原因 解决
传Date收到字符串 Date被序列化为ISO字符串 传时间戳number
传class实例丢失方法 序列化只保留数据字段 只传数据,方法在目标页重建
返回值then不触发 用户手势返回没带参数 用on(‘pop’)监听
大对象传递卡顿 Navigation序列化开销 用AppStorage或单例中转
跨Ability参数丢失 parameters不支持复杂类型 只传基本类型或JSON字符串
参数undefined导致crash 没做空值校验 每个字段都判空
深链接参数是字符串 URI查询参数无类型 手动parseInt/parseFloat
Map/Set传过去变空 不可序列化 转成数组再传
嵌套对象类型丢失 反序列化返回Record 只传ID或逐层as转换
临时文件没清理 tempDir写入后忘记删 aboutToDisappear中清理

传参的核心原则:简单数据用Navigation param,复杂数据用全局状态或单例中转,跨Ability用want parameters,永远只传最小必要信息。 传ID不传对象,传时间戳不传Date,传key不传数据。这不是偷懒,是工程素养。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐