perspective-viewer Web Component 实战指南:Perspective 数据可视化组件的前端集成、主题与配置持久化
2026/9/15 19:11:10 网站建设 项目流程

perspective-viewer Web Component 实战指南:Perspective 数据可视化组件的前端集成、主题与配置持久化

【免费下载链接】perspectiveA data visualization and analytics component, especially well-suited for large and/or streaming datasets.项目地址: https://gitcode.com/GitHub_Trending/pe/perspective

<perspective-viewer>是 Perspective 数据可视化与分析库的官方 UI 组件,它以 Web Component(Custom Element)的形式为浏览器应用提供完整的图形化配置界面,可直接绑定本地 Web Worker、远程 WebSocket 服务端或虚拟数据源中的table(),并将查询结果渲染为数据表格与图表。阅读本文后,你将掌握如何引入并注册该组件、加载与共享数据表、切换主题、通过save()/restore()序列化与恢复视图配置,以及监听更新与点击事件,实现可直接复用的大数据量、流式数据可视化前端方案。

<perspective-viewer>Custom Element 库

<perspective-viewer>为配置perspective库并格式化其输出到各类可视化插件提供了完整的图形化界面。该组件位于 rust/perspective-viewer 包中,npm 包名为@perspective-dev/viewer(版本信息见 rust/perspective-viewer/package.json),与其配套的插件包还包括@perspective-dev/viewer-datagrid(数据表格)与@perspective-dev/viewer-charts(图表)。

引入与注册机制

如果你使用esbuild或其他支持 ES6 模块的打包器,只需在应用的任意位置导入perspective-viewer相关库即可——这些模块不导出任何实际内容,导入的副作用是注册 Custom Element,供站点中的普通 HTML 直接使用:

import "@perspective-dev/viewer"; import "@perspective-dev/viewer-datagrid"; import "@perspective-dev/viewer-charts";

这一“无导出、仅注册”的设计在源码中有明确体现:在 rust/perspective-viewer/src/ts/perspective-viewer.ts 的模块注释中说明,导入该模块的副作用是将PerspectiveViewerElement类注册为 custom element;由于 WebAssembly 编译使该模块成为动态模块,为保证 Custom Element 的扩展方法随包导入同步注册,所有 API 方法都被设计为async(必须等待 wasm 模块实例就绪)。

导入完成后,<perspective-viewer>Web Component 即可用于站点任意标准 HTML 中。一个最简单的例子:

<perspective-viewer id="view1"></perspective-viewer>

或者在 JavaScript 中以编程方式创建:

const viewer = document.createElement("perspective-viewer");

从源码结构看,组件的主入口(custom element 定义、load/save/restore/reset等 API 实现)位于 rust/perspective-viewer/src/rust/custom_elements/viewer.rs,前端引导逻辑在 rust/perspective-viewer/src/ts/bootstrap.ts 与 rust/perspective-viewer/src/ts/perspective-viewer.ts。

Theming 主题系统

perspective-viewer及其配套插件支持主题(Theming)。perspective-viewer内置了多套主题,你可以直接在应用中导入任意主题,所有perspective-viewer将随之应用对应主题:

// 基于 Thought Merchants's Prospective 设计的主题 import "@perspective-dev/viewer/dist/css/pro.css"; import "@perspective-dev/viewer/dist/css/pro-dark.css"; // 其他主题 import "@perspective-dev/viewer/dist/css/solarized.css"; import "@perspective-dev/viewer/dist/css/solarized-dark.css"; import "@perspective-dev/viewer/dist/css/monokai.css"; import "@perspective-dev/viewer/dist/css/vaporwave.css";

或者使用打包了所有默认主题的themes.css

import "@perspective-dev/viewer/dist/css/themes.css";

仓库中的主题源文件集中存放在 rust/perspective-viewer/src/themes,除上述主题外还包含botanical.cssdracula.cssgruvbox.cssgruvbox-dark.cssphosphor.cssdefaults.cssicons.cssintl.css以及聚合入口themes.css,你可以在该目录下查看每个主题的完整 CSS 定义。

如果你选择不自行打包主题,也可以通过 CDN 直接引用,并在 HTML 中通过<link>引入:

<link rel="stylesheet" crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@perspective-dev/viewer/dist/css/pro.css" />

主题自动检测与 resetThemes()

注意上述<link>标签中的crossorigin="anonymous"属性。当从跨域上下文引入主题时,该属性可能是让<perspective-viewer>检测到主题所必需的。如果检测失败——例如在<perspective-viewer>初始化之后才向document添加额外的主题,或因其他任何原因导致主题自动检测失败——你可以通过.resetThemes()方法手动告知<perspective-viewer>可用的主题名称:

// 重新自动检测主题 viewer.resetThemes(); // 显式设置可用主题(这些主题仍必须先作为 CSS 导入!) viewer.resetThemes(["Pro Light", "Pro Dark"]);

resetThemes()的实现位于 rust/perspective-viewer/src/rust/custom_elements/viewer.rs:传入None时重新解析document中的 CSS 以自动检测主题,传入字符串数组时则显式限定可用主题集合;调用后组件会同步当前激活主题,若新集合中不包含当前主题,则可能触发主题切换。与之配套的还有getThemes()方法(同文件第 1704 行),用于查询当前可用的主题名称列表。主题配置的底层状态管理见 rust/perspective-viewer/src/rust/config/viewer_config.rs 与 rust/perspective-viewer/src/rust/presentation.rs 中的reset_available_themes

初始主题设置

<perspective-viewer>初始化时会默认使用第一个加载的主题。你可以通过.restore()覆盖默认值,或通过设置theme属性来提供初始主题:

<perspective-viewer theme="Pro Light"></perspective-viewer>

const viewer = document.querySelector("perspective-viewer"); await viewer.restore({ theme: "Pro Dark" });

<perspective-viewer>加载数据

数据可以通过load()方法,以Table()Promise<Table>的形式加载到<perspective-viewer>中:

// 创建一个新的 worker,然后在 worker 上创建 table promise。 const worker = await perspective.worker(); const table = await worker.table(data); // 将 viewer 元素绑定到这个 table。 await viewer.load(table);

load()的绑定逻辑见 rust/perspective-viewer/src/rust/custom_elements/viewer.rs,它接受一个JsClientLoad(既可以是本地/Worker 端 client,也可以是远程虚拟表 client)并建立会话。

在多个perspective-viewer之间共享一个table()

多个perspective-viewer可以通过把同一个table()传入每个 viewer 的load()方法来共享该表。当底层table()被更新时,每个perspective-viewer都会同步更新;但table.delete()在所有引用它的perspective-viewer实例也被删除之前会失败:

const viewer1 = document.getElementById("viewer1"); const viewer2 = document.getElementById("viewer2"); // 创建一个新的 WebWorker const worker = await perspective.worker(); // 在该 worker 中创建 table const table = await worker.table(data); // 将同一个 table 加载到两个不同的 <perspective-viewer> 元素中 await viewer1.load(table); await viewer2.load(table); // `viewer1` 和 `viewer2` 都会反映这次更新 await table.update([{ x: 5, y: "e", z: true }]);

通过WebSocketServer()与 Node.js 实现仅服务端模式

加载一个虚拟的(仅服务端)Table与加载本地/Web Worker 的Table完全一样——只需将虚拟Table传给viewer.load()

在浏览器端:

const elem = document.getElementsByTagName("perspective-viewer")[0]; // 绑定到服务器的 worker,而不是实例化一个 Web Worker。 const websocket = await perspective.websocket( window.location.origin.replace("http", "ws"), ); // 将 viewer 绑定到预加载的数据源。`table` 和 `view` 对象都驻留在服务器上。 const server_table = await websocket.open_table("table_one"); await elem.load(server_table); // 或者通过 view 加载 table 的数据。浏览器此时在其自身的 `table` 中 // 也拥有一份该 view 的副本,其更新通过 Apache Arrow 传输到浏览器。 const worker = await perspective.worker(); const server_view = await server_table.view(); const client_table = worker.table(server_view); await elem.load(client_table);

以这种方式绑定的<perspective-viewer>实例,与依赖 Web Worker 的<perspective-viewer>在其余方面没有区别,甚至可以与依赖 Web Worker 绑定的table()共处同一个宿主应用中。二者都使用相同的基于 promise 的 API 与服务器端实例化的view()通信,只是此处通过 websocket 进行。仓库中关于服务端集成的完整说明可参见 docs/md/explanation/virtual_servers.md 与 docs/md/explanation/architecture.md。

通过save()/restore()持久化<perspective-viewer>配置

<perspective-viewer>是**持久化(persistent)**的:它除数据本身之外的整个状态都可以被序列化或反序列化。这包括所有列(columns)、过滤器(filters)、透视(pivots)、表达式(expressions)等属性,以及数据表格的样式设置、配置面板的可见性等。这个重载功能覆盖了一系列使用场景:

  • load()调用之后设置<perspective-viewer>的初始状态。
  • 更新单个或部分属性,而不修改其他属性。
  • 将部分或全部属性重置为与数据相关的默认值。
  • 将用户的配置持久化到localStorage或服务器。

序列化与反序列化 viewer 状态

要获取整个状态的 JSON-ready JavaScript 对象,使用save()方法。save()还支持其他格式,如"arraybuffer""string"(base64,而非 JSON),你可以在占用空间与迁移/手工编辑便利性之间做出取舍:

const json_token = await elem.save(); const string_token = await elem.save("string");

save()的 Rust 侧实现见 rust/perspective-viewer/src/rust/custom_elements/viewer.rs:它通过get_viewer_config读取面板当前的ViewerConfig并编码返回。对于任何格式,序列化得到的 token 都可以通过restore()方法恢复到任何具有相同 schema 的Table<perspective-viewer>上。需要注意的是,save()返回的 token 中的数据可能不同,但其 schema 一般不能不同,因为许多其他设置依赖于列名和类型:

await elem.restore(json_token); await elem.restore(string_token);

由于restore()是根据 token 的类型进行分派的,因此务必保证这些类型匹配!一个常见的错误来源是:将 JSON-stringified 的 token 传给restore(),此时字符串 token 会被假定为 base64 编码的 msgpack:

// 这将会报错! await elem.restore(JSON.stringify(json_token));

restore()的实现(含suppress_errors选项与单面板/全面板的分派逻辑)见 rust/perspective-viewer/src/rust/custom_elements/viewer.rs。

更新单个属性

使用 JSON 格式,<perspective-viewer>配置的每一个方面都可以通过 JavaScript 的restore()方法进行操作。属性的合法结构由ViewerConfig与内嵌的ViewConfig类型声明描述(前者见 rust/perspective-viewer/src/ts/ts-rs/ViewerConfig.d.ts,后者见 rust/perspective-js/src/rust 下 client/view 相关类型),关于每个ViewConfig属性的交互式示例可参阅文档的 View 章节。

// 设置插件(同时会把 `columns` 更新为插件默认值) await elem.restore({ plugin: "X Bar" }); // 更新插件和列(只绘制一次) await elem.restore({ plugin: "X Bar", columns: ["Sales"] }); // 打开配置面板 await elem.restore({ settings: true }); // 创建表达式 await elem.restore({ columns: ['"Sales" + 100'], expressions: { "New Column": '"Sales" + 100' }, }); // 如果列不存在于 schema 或 expressions 中则报错 // await elem.restore({columns: ["\"Sales\" + 100"], expressions: {}}); // 添加过滤器 await elem.restore({ filter: [["Sales", "<", 100]] }); // 添加排序,但不移除过滤器 await elem.restore({ sort: [["Prodit", "desc"]] }); // 只重置过滤器,保留排序 await elem.restore({ filter: undefined }); // 将全部属性重置为默认值,例如在 `load()` 之后 await elem.reset();

reset()的实现位于 rust/perspective-viewer/src/rust/custom_elements/viewer.rs:reset_all参数为true时还会清除表达式和列样式设置;可通过{panel: "p1"}选项只重置指定面板,省略时重置所有面板。

另一种快速生成目标配置 token 的有效方式是:在浏览器中手动设置好视图后,直接复制save()返回的 token。JSON 格式是人类可读的,且save()会返回所有属性的默认设置,因此生成的 token 非常容易手工微调。你可以在应用代码中调用save(),也可以通过 Chrome 开发者控制台调用:

// 复制到剪贴板 copy(await document.querySelector("perspective-viewer").save());

对于多面板(workspace)场景,<perspective-viewer>还提供了saveWorkspace()/restoreWorkspace()对,用于序列化/恢复整个 workspace 配置(包括布局、面板与调色板),实现见 rust/perspective-viewer/src/rust/custom_elements/viewer.rs。

更新事件 Update events

每当<perspective-viewer>底层的table()通过load()update()方法发生变化时,会触发perspective-view-updateDOM 事件。类似地,通过 Attribute API 或用户交互触发的view()更新会触发perspective-config-update事件:

elem.addEventListener("perspective-config-update", function (event) { var config = elem.save(); console.log("The view() config has changed to " + JSON.stringify(config)); });

从源码看,事件系统基于 PubSub 通道向 JavaScriptCustomEvent的扇出机制实现,核心代码在 rust/perspective-viewer/src/rust/custom_events.rs:wire_element_events(第 209 行起)负责元素级事件(主题变化、设置面板开关等),wire_panel_events(第 326 行起)负责单个面板的session/renderer通道;所有perspective-*事件均以bubbles+composed方式派发,perspective-config-updatedetail为裸的ViewerConfig(第 147-202 行dispatch_config_update),可直接用于viewer.restore(e.detail)的往返操作。除本文涉及的事件外,该文件还实现了layout-updateactive-panel-updateglobal-filter-updateselectcolumn-style-changetable-delete等事件。

点击事件 Click events

每当<perspective-viewer>的表格或图表被点击时,会触发perspective-clickDOM 事件,其 detail 对象包含configcolumn_namesrow三个字段。

config对象包含一组filters,可以通过restore()应用到<perspective-viewer>,使其显示过滤后的数据子集。

column_names属性包含匹配列的数组,row属性返回对应的行数据。

elem.addEventListener("perspective-click", function (event) { var config = event.detail.config; elem.restore(config); });

在源码中,perspective-click监听器也被用于 master/detail 联动:点击 master 面板(如柱状图的柱子、平面表格的单元格)被视为一次选区贡献,会路由为MasterContribution消息(见 rust/perspective-viewer/src/rust/components/viewer/wiring.rs 与 rust/perspective-viewer/src/rust/components/viewer/msg.rs),从而驱动全局过滤联动——这意味着你可以在自己的应用中基于perspective-click构建跨图表的交互式筛选体验。

结语与延伸阅读

至此,你已经掌握了@perspective-dev/viewer组件从引入注册、数据加载与共享、服务端 WebSocket 绑定、主题定制,到配置序列化持久化与事件响应的完整用法。以上 API 全部建立在asyncpromise 模型之上,既适用于单页应用内的本地 Worker 模式,也适用于与 Python/Rust 服务端配合的远程模式。

如果你想进一步深入,可以继续阅读仓库中的以下资料:

  • docs/md/explanation/view.md:ViewConfig各属性的交互式示例
  • docs/md/how_to/javascript/virtual_server.md:JavaScript 端虚拟服务器接入
  • docs/md/how_to/javascript/save_restore.md:保存与恢复状态的进阶用法
  • docs/md/how_to/javascript/theming.md:主题定制的详细说明
  • docs/md/how_to/javascript/events.md:完整事件清单
  • rust/perspective-viewer/src/ts:组件 TypeScript 侧入口与类型声明
  • rust/perspective-viewer/src/rust:组件 Rust 侧实现源码

【免费下载链接】perspectiveA data visualization and analytics component, especially well-suited for large and/or streaming datasets.项目地址: https://gitcode.com/GitHub_Trending/pe/perspective

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

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

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

立即咨询