@exodus/atoms

中文文档
10.3.5发布于 2026/10/10UNLICENSEDTypeScriptES Module近 30 天下载 3.5万

将数据封装在一个简单的统一界面后:获取、设定、观察

中文使用文档
文档目录12 节
  1. 安装
  2. 什么是原子吗?
  3. 数据来源
  4. 解决问题
  5. 使用量
  6. 增强器
  7. 计算( {原子, 选择器} ): 只读原子
  8. 组合({ [键]:原子}):只读原子
  9. dedupe( 原子)
  10. 与序列化({原子、序列化、去序列化})
  11. 相差( 原子)
  12. 过滤器( 原子, 上游)

@exodus/atoms

安装

    pnpm add @exodus/atoms

什么是原子吗?

原子是一个数据源包,它通过三种不同的方法暴露出单个数据:

  • 获取(): 读取数据
  • 设置( 新数值):设置原子值,屏蔽直到所有观察者解决.
  • 观察( 同步( 数据) ) qq ):观察数据变化。 最初将使用当前数据值。 观察员正在等待一系列安排.
  • 重设( c):清除存储值。 下一个 get() 调用将返回默认值*,并用默认值调用观察者.

数据来源

这个库为从我们应用中所使用的多个数据源创建原子提供了帮助.

起来设定观察
内存✅✅✅
存储✅✅✅
键盘✅🟡 *✅
事件发射器✅❌✅

*一个键盘原子需要一个特殊的 isSoleWriter 允许写入的参数.

另见:

注:这所图书馆最初曾主办一大批媒体特有工厂,后被搬出,如上两所. 以上可能也随之而来,而这个库只执行常见的媒体不可知原子行为.

解决问题

从理论上讲,所有原子都应该有类似的行为. 在实践中,目前有一些不一致的行为,我们的目标是在未来加以纠正,特别是围绕一个事件发射器产生的记忆原子和原子:

  • 内存原子挂起 get() 若无 defaultValue 提供.
  • 内存和事件 Emitter 原子观测器是非屏蔽的,即:. memoryAtom.set() 火和遗忘

使用量

import { EventEmitter } from 'events'
import {
  createInMemoryAtom,
  createStorageAtomFactory,
  fromEventEmitter,
  createKeystoreAtom,
} from '@exodus/atoms'

// In memory atoms
const availableAssetNamesAtom = createInMemoryAtom({
  defaultValue: {},
})

// Storage atoms
const storageAtomFactory = createStorageAtomFactory({ storage })

const acceptedTermsAtom = storageAtomFactory({
  key: 'acceptedTerms',
  defaultValue: false,
  isSoleWriter: true,
})

// Event emitter
const geolocation = new EventEmitter()
const geolocationAtom = fromEventEmitter({
  emitter: geolocation,
  event: 'geolocation',
  get: async () =>
    new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject)),
})

navigator.geolocation.watchPosition((position) => {
  geolocation.emit('geolocation', position)
})

// keystore
const keystoreAtom = createKeystoreAtom({
  keystore, // see @exodus/keystore-mobile
  config: {
    key: 'my-secret',
    defaultValue, // optional
    isSoleWriter, // if you plan to call set() on this atom instance
  },
})

增强器

为了计算衍生值,将多个原子结合到其他有用的衍生物中,并进行其他有用的衍生,可以使用一串增强子. 下面是一份并非详尽无遗的清单,请检查一下 ./弧/增强 为了更多.

计算( {原子, 选择器} ): 只读原子

通过对观测到的数据源应用一个选择器函数,从另一个原子中计算一个原子.

示例

import { createInMemoryAtom, compute } from '@exodus/atoms'

const yearAtom = createInMemoryAtom({ defaultValue: 2025 })

const isDoorsOfStoneOutYetAtom = compute({
  atom: yearAtom,
  selector: (year) => year > 2040,
})

isDoorsOfStoneOutYetAtom.observe(console.log) // false

组合({ [键]:原子}):只读原子

将多个原子组合为一个原子 :

  • combinedAtom.observe:在所有原子释放出值时,首次起火.
  • combinedAtom.get: 解决输入中包含所有原子值的对象.

示例

import { createInMemoryAtom, combine } from '@exodus/atoms'

const nameAtom = createInMemoryAtom()
const ageAtom = createInMemoryAtom()
const userAtom = combine({
  name: nameAtom,
  age: ageAtom,
})

userAtom.observe(console.log) // hangs until both name and age are set
nameAtom.set('Voldemort')
ageAtom.set(25)
// userAtom atom fires with { name: 'Voldemort', age: 25 }

dedupe( 原子)

默认情况下,原子进行浅等检查,以确定新写值与当前值是否不同,并避免通知不通知观察者. 如果你想要深刻的平等检查,使用 dedupe.(更妙的是,首先不要给原子写出非常等同的物体,而且不要使用 dedupe) (中文(简体) )

示例

import { createInMemoryAtom, dedupe } from '@exodus/atoms'

const userAtom = createInMemoryAtom({ defaultValue: { name: 'Voldemort', age: 25 } })
const dedupedUserAtom = dedupe(userAtom)
userAtom.set({ name: 'Voldemort', age: 25 }) // `userAtom` observers are notified
dedupedUserAtom.set({ name: 'Voldemort', age: 25 }) // `dedupedUserAtom` observers are NOT notified

与序列化({原子、序列化、去序列化})

如果你将数据存储在一个需要(去)序列化的原子中,例如存储原子,而数据通过JSON.stringify / JSON在回转中无法存活. 剖析, 使用 withSerialization 提供自定义序列化.

示例

import BJSON from 'buffer-json'
import { createInMemoryAtom, withSerialization } from '@exodus/atoms'

const rawPublicKeysAtom = createInMemoryAtom()
const publicKeysAtom = withSerialization({
  atom: rawPublicKeysAtom,
  serialize: BJSON.stringify,
  deserialize: BJSON.parse,
})

publicKeysAtom.set({
  bitcoin: Buffer.from([...]),
  ethereum: Buffer.from([...]),
})

相差( 原子)

如果您想要同时获得原子释放的当前值和先前值,请使用 difference。 。 。 .

示例

import { createInMemoryAtom, difference } from '@exodus/atoms'

const nameAtom = createInMemoryAtom({ defaultValue: 'Tom' })
const nameChangeAtom = difference(nameAtom)
nameChangeAtom.observe(console.log)
nameAtom.set('Voldemort')
// nameChangeAtom emits
// { previous: 'Tom', current: 'Voldemort' }

过滤器( 原子, 上游)

如果你只对一个子集的数值感兴趣 一个原子的分集,使用 filter数字 :

示例

import { createInMemoryAtom, filter } from '@exodus/atoms'

const nameAtom = createInMemoryAtom({ defaultValue: 'Tom' })
const unusualNameAtom = filter(nameAtom, (name) => !['Tom', 'Dick', 'Harry'].includes(name))
unusualNameAtom.observe(console.log)
nameAtom.set('Dick') // unusualNameAtom doesn't emit
nameAtom.set('Harry') // unusualNameAtom doesn't emit
nameAtom.set('Voldemort') // unusualNameAtom emits 'Voldemort'

版本与发布记录

@exodus/atoms 共发布 84 个版本,首次发布于 2022/07/26;过去 12 个月发布了 10 个版本。

版本发布日期距今
10.3.5latest2026/10/101 个月前
10.3.42026/04/137 个月前
10.3.32026/04/077 个月前
10.3.22026/04/067 个月前
10.3.12026/04/057 个月前
10.3.02026/04/037 个月前

关于 @exodus/atoms 的常见问题

@exodus/atoms 是什么?

将数据封装在一个简单的统一界面后:获取、设定、观察

如何安装 @exodus/atoms?

在项目目录运行 npm install @exodus/atoms,或使用 pnpm add / yarn add / bun add。

@exodus/atoms 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@exodus/atoms 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@exodus/atoms 可以商用吗?

@exodus/atoms 使用 UNLICENSED 许可。作者声明为 UNLICENSED,即不授予公开使用许可(与公共领域的 Unlicense 不同)。使用前需获得作者授权。以上为概括,不构成法律意见,请以许可原文为准。

@exodus/atoms 的下载量有多少?

根据 npm 官方下载统计,@exodus/atoms 在 2026/10/14 – 2026/11/12 这 30 天内被下载 35,420 次,日均约 1,181 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@exodus/atoms 还在维护吗?

最新版本 10.3.5 发布于 2026/10/10(1 个月前);至今共发布 84 个版本,其中过去 12 个月发布了 10 个;首次发布于 2022/07/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。