下一代 Expo API:MediaLibrary 和 Contacts

SDK 55 带来了核心库的下一代版本。Shared Objects 与 Shared Refs 从根本上改变了使用这些库与原生数据交互的方式。

中文
复制
The Next Generation of Expo APIs: MediaLibrary and Contacts

我们认为 Shared Objects 和 Shared Refs 是 Expo 库的未来,因为它们能让模块之间实现更深入的集成,而不必引入不必要的耦合。想进一步了解 shared objects 和 shared refs,可以看这篇文章

在 SDK 54 中,我们更新了 File System,换上了新的面向对象 API。现在轮到 Contacts 和 MediaLibrary 了。更新后的版本分别以 expo-contacts/nextexpo-media-library/next 提供。后续的 SDK 版本中,我们会继续把这一套带到其他库。

下面来看看 contacts 和 media library 这两个包具体改了什么。

MediaLibrary@Next

核心概念

新 API 围绕一个简单的想法构建:AlbumAsset 现在是类,充当原生数据的代理。它们持有系统相册中对象的引用,方便随时获取元数据。

要创建新 asset,需要把本地 URI 传给静态函数 Asset.create()。这个调用会把文件加入相册,并返回一个 shared object:

// Create an asset from a local URI
const asset = await Asset.create('file:///path/to/photo.png');

拿到 asset 之后,管理起来就直观多了:

// Fetch an album and add the asset directly
const album = await Album.get('Holiday 2026');
await album.add(asset);

// Or create a new album and add the asset in one go
const album = await Album.create('Holiday 2026', [asset]);

直接访问属性

旧 API 常见的瓶颈在于 getAssetInfoAsync() 返回的对象体积。你只是想看一下位置,却不得不先取回一个包含所有属性的庞大普通对象。MediaLibrary@Next 提供了细粒度的 getter,让你只取需要的部分。再也不用为了看一张图片的尺寸而拉取整个元数据对象。

await asset.getLocation(); // { longitude: 50, latitude: 20 }
await asset.getShape(); // { width: 100, height: 100 }

// Need everything? You can still do that:
await asset.getInfo();

强大的查询

MediaLibrary@Next 引入了新的媒体筛选方式。新增的 Query 类借鉴了 builder 模式,让你能以可读性很高的方式构造更精确的查询。

它支持以下谓词:gtgteltelteqwithinalbum,另外还有用于排序的 orderBy,以及用于分页的 limit/offset

const assets = await new Query()
  .eq(AssetField.MEDIA_TYPE, MediaType.IMAGE)
  .lte(AssetField.HEIGHT, 1080)
  .within(AssetField.WIDTH, [920, 960, 1080])
  .orderBy(AssetField.CREATION_TIME)
  .limit(20)
  .offset(10)
  .exe();

Contacts@Next

核心概念

新 API 的设计基于以下前提:

Contacts@Next 中的 contact 对象持有系统联系人的引用,并包含大量异步函数,用于读取或更新底层系统联系人的状态。与旧 API 不同,你不必在每次函数调用时手动传入 ID,只需在特定 contact 实例上调用 update 即可,这样能避免很多容易犯的错误。

你可以用静态方法创建、获取或选取联系人,快速上手:

// Create a new contact
const contact = await Contact.create({
  givenName: 'Andrew',
  familyName: 'Jones',
  phones: [
    {
      label: 'mobile',
      number: '+12123456789',
    },
  ],
});

// Fetch existing contact
const [contact] = await Contact.getAll({ limit: 1 });

// Or pick one using the system UI
const contact = await Contact.presentPicker();

细粒度更新

没人愿意为了改一个属性就去摆弄庞大的 JavaScript 对象。比如在旧版 API 里,想给某个联系人加一个新邮箱地址,你得改完整个对象再把它发回去:

// THE OLD WAY
const updatedContact = {
  ...contact,
  emails: [...(contact.emails || []), { address: 'contacts-next@expo.dev', label: 'work' }],
};
await Contacts.updateContactAsync(updatedContact);

现在同样的操作只要一行代码:

// THE NEW WAY
await contact.addEmail({ address: 'contacts-next@expo.dev', label: 'work' });

而且每个联系人属性都能这么改!看看现在设置头像有多简单:

const result = await ImagePicker.launchImageLibraryAsync();
await contact.setImage(result.assets[0].uri);

高性能查询

查询也是一样——如果你只想取某一个特定字段,可以用细粒度的 getter:

const givenName = await contact.getGivenName();

要取多个属性,就用 getDetails。把关心的字段放进数组传进去即可:

await contact.getDetails([ContactField.EMAILS, ContactField.FULL_NAME]);

如果你在构建联系人列表,还有一个更高效的方法:getAllDetails()。它在底层只发起一次系统 API 调用,性能非常好,还支持通过可选参数做不同类型的过滤。

// This is a static module function.
await Contact.getAllDetails([ContactField.EMAILS, ContactField.FULL_NAME]);

另外,这些方法返回的对象会根据参数里请求的字段收窄类型。如果你访问了没有取回来的字段,TypeScript 会给出警告。

const details = await contact.getDetails([ContactField.GIVEN_NAME]);

console.log(details.givenName); // ✅ "John"
console.log(details.phones); // ❌ TypeScript will show a warning

.patch() + .getDetails() = <3

新版库加入了一个 patch() 方法——和 HTTP 协议里的那个类似——用来做局部修改。它会跳过值为 undefined 的属性,所以你只会更新真正想改的字段。

它的设计目标就是和 getDetails() 顺畅配合:getDetails() 只取请求的字段,patch() 只更新已定义的字段。对于只改一两个字段的表单,这再合适不过:

const details = await contact.getDetails([ContactField.EMAILS, ContactField.GIVEN_NAME]);
details.givenName = 'John';
await contact.patch(details); // Only updates the givenName in the system.

如果你想彻底替换联系人属性,仍然可以用 update 方法。和 patch 不同,它会覆盖整个联系人:

await contact.update({
  givenName: 'New Name',
  familyName: 'New Surname',
  // Other fields will be cleared
});

看看新文档

这已经是这些库的 next 个迭代了,所以你的反馈比以往任何时候都重要。欢迎提任何意见、GitHub issue、功能请求,或者发推给我们,我们非常感激。

也欢迎你查看新文档,里面提供了 API 实际用法的示例!

来源: Expo Blog← 返回首页