- 图形学
- 前端
【免费下载链接】two.js
A renderer agnostic two-dimensional drawing api for the web
Two.Registry 是 Two.js 中一个轻量级的通用"目录管理"类,以字符串id为键维护一张键值映射表。它在 Two.js 内部主要承担纹理(Texture)缓存、SVG<defs>解析缓存与 WebGL 纹理注册等关键职责。读完本文你将掌握Registry的完整 API、链式调用风格,以及它在图像去重、SVG 资源复用与 GPU 纹理管理中的具体工作方式。
一、什么是 Two.Registry
从 源码定义 可以确认,Two.Registry是一个极简的通用注册表容器,官方描述为:
An arbitrary class to manage a directory of things. Mainly used for keeping tabs of textures in Two.js.
即"用于管理一组对象目录的任意类,主要用于追踪 Two.js 中的纹理"。它在内部用一个普通对象map存储数据,通过唯一的字符串id完成增、删、查等操作。由于其"目录"语义与集合类(Collection)不同——Registry 更接近一个按需可寻址的字典(dictionary),而不是一个可迭代渲染的对象列表。
从代码结构来看,整个类非常精简,只包含一个属性与四个方法:
- 属性
map:内部存储对象,初始为空对象{}; - 方法
add、remove、get、contains。
值得注意的是,Registry本身与渲染无关、与图形绘制无关,它不依赖 DOM、不依赖渲染器,因此可以在 Node.js 无头(headless)环境下正常使用。
二、如何获取 Two.Registry
在 src/two.js 的类导出区域中,Registry被挂载为Two的静态属性:
static Registry = Registry; // src/two.js#L334因此在你引入 Two.js 后,可以直接通过Two.Registry访问:
import Two from 'two.js'; const registry = new Two.Registry();同时,Registry也作为独立模块存在于仓库中,可以从 src/registry.js 直接导入,其类型声明见 src/registry.d.ts:
declare module 'two.js/src/registry' { export class Registry { map: {}; add(id: string, obj: any): Registry; remove(id: string): Registry; get(id: string): any | null; contains(id: string): boolean; } }从类型签名可以看出:add与remove都会返回Registry自身,这是刻意设计的**链式调用(method chaining)**接口。
三、API 详解
3.1 add(id, obj):注册任意值
add(id, obj) { this.map[id] = obj; return this; }| 参数 | 类型 | 说明 |
|---|---|---|
| id | String | 唯一标识符 |
| obj | Any | 要注册进目录的任意类型变量 |
add将任意类型的值写入目录,并以id为键进行索引。赋值完成后返回this,便于链式调用多个注册操作:
registry .add('logo', imageElement) .add('cursor', cursorElement);两点使用注意:
id是字符串键。如果传入非字符串(例如数字),JavaScript 会将其隐式转为字符串后存入map;- 使用相同的
id重复add会覆盖旧值,不会报错也不会合并。因此若要保证不覆盖,建议先调用contains判断。
3.2 remove(id):按 id 移除
remove(id) { delete this.map[id]; return this; }| 参数 | 类型 | 说明 |
|---|---|---|
| id | String | 唯一标识符 |
remove通过delete操作符从map中彻底删除该键。删除不存在的键也不会报错,且同样返回this以支持链式调用。
需要注意的是:delete与"将值置为undefined"并不等价——前者会移除键本身,因此后续调用contains会返回false;而后者键仍然存在,contains依然会返回true。这正是内部使用id in this.map(而非this.map[id] !== undefined)判断存在性的原因。
3.3 get(id):按 id 取值
get(id) { return this.map[id]; }| 参数 | 类型 | 说明 |
|---|---|---|
| id | String | 唯一标识符 |
get返回与id关联的值。如果该id从未注册过,则返回undefined。官方文档明确注明:
返回值:Object —— 关联的值;若不可用则返回
undefined。
在 TypeScript 声明中该返回类型为any | null,实际运行时体现为undefined。使用时建议配合contains或直接做空值判断:
const cached = registry.get('bg-image'); if (registry.contains('bg-image')) { // 缓存命中,直接复用 }3.4 contains(id):存在性检查
contains(id) { return id in this.map; }| 参数 | 类型 | 说明 |
|---|---|---|
| id | String | 唯一标识符 |
返回值:Boolean。这是官方文档所述"查看某个值是否已注册到某个id的便捷方法"。它使用in运算符判断键是否存在,语义上等价于map.hasOwnProperty(id)的一层简化,且能正确处理"值为undefined但键已存在"的边界情况。该方法是 Two.js 内部所有缓存命中判断的基石。
四、内部核心应用场景
虽然Registry本身是通用容器,但它在 Two.js 中承担着三个关键内部职责,深入理解这些场景才能真正理解它的设计价值。
4.1 场景一:Two.Texture.ImageRegistry —— 图像会话级缓存
这是 Registry 在 Two.js 中最核心的应用。在 src/effects/texture.js 中,纹理类持有静态注册表:
static ImageRegistry = new Registry();其用途正如源码注释所述:"一份用于单个 Two.js 会话中图像数据的权威清单(canonical listing),用于在不同纹理之间缓存图像数据。"
典型调用链如下:
Texture.getImage(src)先调用getAbsoluteURL将相对路径归一化为绝对 URL(通过临时anchor元素解析);- 然后判断缓存:
Texture.ImageRegistry.contains(absoluteSrc),命中则直接get返回缓存的HTMLImageElement; - 未命中时创建新的
img/video元素,并在加载完成后执行Texture.ImageRegistry.add(texture.src, image)登记。
对应源码见 src/effects/texture.js 与 src/effects/texture.js。
这套缓存机制的价值在于:多个Two.Texture若引用同一个图片 URL,最终只会创建一份HTMLImageElement,从而显著节省内存与网络请求。这种"以规范化的绝对 URL 为键、以真实图片元素为值"的缓存模式,正是 Registry 键值语义的典型体现。
4.2 场景二:SVG 解析中的 defs 缓存
在 Two.js 的 SVG 解释器中,Registry用于缓存 SVG<defs>段中的定义资源。见 src/utils/interpret-svg.js:
svg: function (node) { const defs = (read.defs.current = new Registry()); ... }解析器遍历 SVG 文档中的<defs>元素,将渐变(gradient)、裁剪路径等定义按id写入该 Registry,供后续fill、stroke属性引用时快速查找。该缓存还会被导出为svg.defs,供上层代码在解析完成后继续使用。
4.3 场景三:WebGL 渲染器的 TextureRegistry
在 src/renderers/webgl.js 中,WebGL 渲染器拥有一个独立的纹理注册表:
TextureRegistry: new Registry(),该注册表用于追踪已上传到 GPU 的纹理,实现纹理对象的统一登记与复用,是 WebGL 渲染器管理图形资源的一部分。这再次印证了 Registry 的通用性——同一容器被复用于浏览器图像缓存、SVG 定义缓存与 GPU 纹理管理三类差异巨大的场景。
五、使用示例
5.1 基本用法
import Two from 'two.js'; const registry = new Two.Registry(); // 注册 registry.add('circle', { radius: 20 }); registry.add('rect', { width: 10, height: 5 }); // 查询 registry.contains('circle'); // true registry.get('circle'); // { radius: 20 } // 覆盖 registry.add('circle', { radius: 40 }); registry.get('circle'); // { radius: 40 } // 移除 registry.remove('circle'); registry.contains('circle'); // false registry.get('circle'); // undefined // 链式调用 registry.add('a', 1).add('b', 2).remove('a');5.2 复刻图像缓存模式
可以仿照Texture.ImageRegistry的成熟模式,用 Registry 为高频资源做缓存:
import Two from 'two.js'; const cache = new Two.Registry(); function getAsset(url) { if (cache.contains(url)) { return cache.get(url); // 命中缓存 } const asset = loadAsset(url); // 昂贵的加载过程 cache.add(url, asset); return asset; }要点在于:以可规范化的唯一标识(URL/绝对路径)为键,命中即复用,未命中则创建并登记。这既是 Two.js 自身的实践,也是一个通用且健壮的内存缓存范式。
六、源码速览与深入学习
- 类实现:src/registry.js(完整实现仅约 57 行)
- 类型声明:src/registry.d.ts
- 挂载位置:src/two.js(
Two.Registry静态属性) - 图像缓存应用:src/effects/texture.js(
Texture.ImageRegistry) - SVG defs 缓存应用:src/utils/interpret-svg.js
- WebGL 纹理注册:src/renderers/webgl.js
七、小结
Two.Registry用最小的实现(一个map、四个方法)为 Two.js 提供了统一的键值目录能力:
- 通用:可注册任意类型的值,不依赖 DOM 与渲染器,无头环境下可用;
- 链式:
add/remove返回this,支持流畅的连续操作; - 明确语义:
contains基于in运算符判断键存在性,与"值为 undefined"严格区分; - 内部复用:ImageRegistry(图像缓存)、SVG defs(定义缓存)、WebGL TextureRegistry(GPU 纹理登记)三处核心场景共享同一套机制。
理解 Registry,就理解了 Two.js 中"以唯一标识索引共享资源"的底层设计哲学。如果你正在为 Two.js 应用编写资源管理、缓存复用或插件化的扩展代码,Two.Registry是一个开箱即用的可靠选择。
- 图形学
- 前端
【免费下载链接】two.js
A renderer agnostic two-dimensional drawing api for the web
相关推荐
CircleType.js实现原理揭秘:从数学公式到网页文字弯曲效果
CircleType.js实现原理揭秘:从数学公式到网页文字弯曲效果 CircleType.js是一款让网页文字实现弯曲效果的JavaScript库,它通过精妙
前端Substrate 镜像缓存测试基石:深入 go-containerregistry pkg/registry 内存 OCI Registry 实现
Substrate 镜像缓存测试基石:深入 go containerregistry pkg/registry 内存 OCI Registry 实现 导读 本文
人工智能AI AgentAgent 沙箱云原生容器运行时零信任深度解析 Spring SimpleAliasRegistry:Bean 别名注册与解析的底层实现
深度解析 Spring SimpleAliasRegistry:Bean 别名注册与解析的底层实现 导读 :本文基于 docs/Spring/clazz/Spr
文档教程知识库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考