☰
Two.js Registry 注册表深入解析:图像缓存、SVG defs 与 WebGL 纹理的底层字典实现
2026/9/25 13:05:27 网站建设 项目流程
  • 图形学
  • 前端

【免费下载链接】two.js

A renderer agnostic two-dimensional drawing api for the web

项目地址:https://gitcode.com/gh_mirrors/tw/two.js
点击查看免费下载

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; }
参数类型说明
idString唯一标识符
objAny要注册进目录的任意类型变量

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; }
参数类型说明
idString唯一标识符

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]; }
参数类型说明
idString唯一标识符

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; }
参数类型说明
idString唯一标识符

返回值: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),用于在不同纹理之间缓存图像数据。"

典型调用链如下:

  1. Texture.getImage(src)先调用getAbsoluteURL将相对路径归一化为绝对 URL(通过临时anchor元素解析);
  2. 然后判断缓存:Texture.ImageRegistry.contains(absoluteSrc),命中则直接get返回缓存的HTMLImageElement;
  3. 未命中时创建新的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

项目地址:https://gitcode.com/gh_mirrors/tw/two.js
点击查看免费下载
上一篇:IPTVnator 跨平台嵌入式 MPV:Windows x64 与 Linux x64 原生后端落地指南
下一篇:5分钟快速上手:跨平台资源下载神器res-downloader完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询