Univer 表格查找替换(Find Replace)能力一键集成:@univerjs/preset-sheets-find-replace 使用与 Facade 编程实战
2026/9/14 20:02:16 网站建设 项目流程

Univer 表格查找替换(Find & Replace)能力一键集成:@univerjs/preset-sheets-find-replace 使用与 Facade 编程实战

【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univer

@univerjs/preset-sheets-find-replace是 Univer 面向表格场景提供的高层预设包,它把通用的查找替换基础设施(@univerjs/find-replace)与表格专用能力(@univerjs/sheets-find-replace)打包成一个开箱即用的插件集合,通过一行UniverSheetsFindReplacePreset()即可接入createUniver。本文以该预设包为主线,结合仓库源码讲解其安装、配置、内部插件组合关系,并给出基于 Facade API(createTextFinderAsync/FTextFinder)的查找、遍历、替换完整实战示例。

包概览:一个预设,三项能力

根据 presets/packages/preset-sheets-find-replace/README.md 中的 Package Overview,该预设包的能力清单如下:

PackageCSSLocalesFacade entry
@univerjs/preset-sheets-find-replaceYesYesYes

也就是说,安装这一个包即可获得:

  • CSS 样式:查找替换对话框、高亮等 UI 样式随包内置,无需额外引入样式文件;
  • 多语言资源(Locales):包含完整语言文案,可合并进 Univer 的 locale 映射;
  • Facade 入口:通过univerAPI直接调用createTextFinderAsync等高级 API 进行程序化查找替换。

相比手动注册底层插件,预设包显著降低了接入门槛,是 presets 体系中面向"某一完整功能域"的标准封装方式。

安装与版本约束

从 presets/packages/preset-sheets-find-replace/package.json 可见,该包当前版本为1.0.0-beta.2,采用 pnpm workspace 管理(依赖以workspace:*形式声明)。安装命令与原文档一致:

pnpm add @univerjs/preset-sheets-find-replace # or npm install @univerjs/preset-sheets-find-replace

需要注意两个版本层面的约束:

  1. 同版本原则:原文档明确要求"Keep all@univerjs/*packages on the same version"——所有@univerjs/*包应保持同一版本,避免因跨版本间的内部类型与协议不一致导致运行时问题。
  2. Peer 依赖:该预设包以 peerDependencies 形式依赖react(^16.9.0 ~ ^19)、react-dom以及rxjs(>=7.0.0),安装时宿主项目需自行满足这些版本要求。

该预设包本身的依赖非常简单,仅包含两个业务依赖(见 package.json):

  • @univerjs/find-replace:提供共享的查找替换服务与 UI 基础设施;
  • @univerjs/sheets-find-replace:将查找替换能力扩展到工作表(Worksheet)场景。

快速接入:在 createUniver 中启用预设

原文档给出的用法示例极为精简:

import { UniverSheetsFindReplacePreset } from '@univerjs/preset-sheets-find-replace'; // Use with createUniver: // createUniver({ presets: [UniverSheetsFindReplacePreset()] });

仓库中的真实示例 examples/src/preset-sheets-core/main.ts 展示了更完整的接入方式,包括预设与本地化资源的组合:

import { createUniver, defaultTheme, LocaleType, mergeLocales } from '@univerjs/presets'; import { UniverSheetsFindReplacePreset } from '@univerjs/preset-sheets-find-replace'; import UniverPresetSheetsFindReplaceZhCN from '@univerjs/preset-sheets-find-replace/locales/zh-CN'; const { univer, univerAPI } = createUniver({ locale: LocaleType.ZH_CN, locales: { zhCN: mergeLocales( // ...其他预设的 locale 资源 UniverPresetSheetsFindReplaceZhCN, // ... ), }, theme: defaultTheme, presets: [ // ...其他预设 UniverSheetsFindReplacePreset(), ], });

要点说明:

  • UniverSheetsFindReplacePreset()是一个工厂函数,返回IPreset结构,可直接放入createUniverpresets数组;
  • 预设函数接受一个可选的Partial<IUniverSheetsFindReplacePresetConfig>配置参数(默认{}),目前该配置接口为空(见 preset.ts),即当前版本无需传任何配置即可使用;
  • 中文环境下,应将@univerjs/preset-sheets-find-replace/locales/zh-CN通过mergeLocales合并进locales.zhCN,确保查找替换对话框等界面文案正确显示。

预设内部结构:两个插件的自动组合

预设之所以"一行接入",是因为它在内部替你完成了插件编排。查看 src/preset.ts:

export function UniverSheetsFindReplacePreset(_config: Partial<IUniverSheetsFindReplacePresetConfig> = {}): IPreset { return { plugins: [ [UniverFindReplacePlugin], [UniverSheetsFindReplacePlugin], ].filter((v) => !!v) as IPreset['plugins'], }; }

即该预设展开后等价于依次注册两个插件:

  1. UniverFindReplacePlugin(来自@univerjs/find-replace):提供跨产品共用的查找替换服务(IFindReplaceService)、FindReplaceModel、对话框 UI 组件与命令/操作层,是能力底座;
  2. UniverSheetsFindReplacePlugin(来自@univerjs/sheets-find-replace):面向表格的扩展层,负责工作表内容提供器(Provider)、命中单元格导航与高亮等。

同时,src/preset.ts 中还执行了import '@univerjs/sheets-find-replace/facade'并把@univerjs/sheets-find-replace/facade的类型重新导出,这正是该预设包 Facade entry 为 Yes 的原因——引入预设即同时激活univerAPI.createTextFinderAsync扩展点。

UniverSheetsFindReplacePlugin的声明(见 packages/sheets-find-replace/src/plugin.ts)可以确认它的完整依赖链:

UniverSheetsFindReplacePlugin └─ DependsOn: UniverRenderEnginePlugin, UniverSheetsPlugin, UniverFindReplacePlugin, UniverSheetsUIPlugin

也就是说,表格查找替换是在渲染引擎、工作表内核、共享查找替换服务与表格 UI 之上工作的。如果你使用UniverSheetsCorePreset()等基础预设,通常这些依赖已就绪;若按旧式univer.registerPlugin(...)手动注册,则需自行保证依赖插件已先注册。

作为对照,不使用预设时的等价手动写法是(见 packages/find-replace/README.md 与 packages/sheets-find-replace/README.md):

import { UniverFindReplacePlugin } from '@univerjs/find-replace'; import { UniverSheetsFindReplacePlugin } from '@univerjs/sheets-find-replace'; univer.registerPlugin(UniverFindReplacePlugin); univer.registerPlugin(UniverSheetsFindReplacePlugin); // 记得合并 EnUS / zh-CN 等 locale 资源

预设包的价值正在于此:把上述步骤压缩为一次调用,同时通过sideEffects: ["*.css"](见 package.json)保证样式自动随包引入。

程序化查找替换:createTextFinderAsync 与 FTextFinder

该预设包提供的 Facade 入口是univerAPI.createTextFinderAsync(text),返回一个FTextFinder实例。其声明位于 packages/sheets-find-replace/src/facade/f-univer.ts:

export class FUniverSheetsFindReplaceMixin extends FUniver implements IFUniverSheetsFindReplaceMixin { override async createTextFinderAsync(text: string): Promise<FTextFinder | null> { const state: Partial<IFindReplaceState> = { findString: text }; const textFinder = this._injector.createInstance(FTextFinder, state); await textFinder.ensureCompleteAsync(); return textFinder; } }

内部实现要点(结合 f-text-finder.ts):

  • FTextFinder会从IFindReplaceService获取所有已注册的查找提供器(getProviders()),并创建独立的FindReplaceModel
  • 每次createTextFinderAsync都会立即执行一次完整的查找(ensureCompleteAsync),所以返回的 textFinder 已经是"查找完成"状态,可直接遍历结果;
  • 查找结果被封装为FRange,可以继续使用表格 Facade 的链式 API(如getA1Notation()getValues()setValues())。

FTextFinder 方法一览

以下方法均有完整 JSDoc 与可运行示例,见 packages/sheets-find-replace/src/facade/f-text-finder.ts:

方法返回值说明
findAll()FRange[]获取当前工作表所有命中单元格;当前命中为最后一个命中项
findNext()Nullable<FRange>移动到下一个命中项并返回其范围
findPrevious()Nullable<FRange>移动到上一个命中项并返回其范围
getCurrentMatch()Nullable<FRange>获取当前命中项范围;查找未完成时会抛出异常
matchCaseAsync(matchCase)Promise<IFTextFinder>开关大小写敏感匹配,内部会触发重新查找
matchEntireCellAsync(matchEntireCell)Promise<IFTextFinder>开关"匹配整个单元格内容"
matchFormulaTextAsync(matchFormulaText)Promise<IFTextFinder>切换按公式文本(而非计算结果)匹配
replaceAllWithAsync(replaceText)Promise<number>全部替换,返回被替换的次数
replaceWithAsync(replaceText)Promise<boolean>仅替换当前命中项
ensureCompleteAsync()Promise<Nullable<IFindComplete>>确保查找完成,切换工作表后需重新调用

查找全部命中项

以下示例取自 f-text-finder.ts 的 JSDoc,演示了写入数据后用createTextFinderAsync找出所有包含 "5" 的单元格:

const fWorkbook = univerAPI.getActiveWorkbook(); const fWorksheet = fWorkbook.getSheetByName('Sheet1'); if (!fWorksheet) return; const fRange = fWorksheet.getRange('A1:D10'); fRange.setValues([ [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], // ...共 10 行对角递增数据 ]); // 查找文本 '5' const textFinder = await univerAPI.createTextFinderAsync('5'); // 获取所有命中单元格 const matchCells = textFinder.findAll(); matchCells.forEach((cell) => { console.log(cell.getA1Notation()); // D2, C3, B4, A5 });

顺序遍历:findNext / findPrevious / getCurrentMatch

Facade 层对命中项实现了"当前命中游标"语义(内部由FindReplaceModel维护currentMatch$)。官方 JSDoc 示例(见 f-text-finder.ts)演示了方向遍历:

const textFinder = await univerAPI.createTextFinderAsync('5'); console.log(textFinder.getCurrentMatch().getA1Notation()); // 初始命中 A5 const nextMatch = textFinder.findNext(); console.log(nextMatch.getA1Notation()); // D2 console.log(textFinder.getCurrentMatch().getA1Notation()); // 当前命中已移动至 D2

对应地,findPrevious()会向反方向移动并返回上一个命中项。若已无上一项/下一项,返回null

大小写、整格与公式文本三种匹配模式

FTextFinder提供了三个可编程的匹配开关,均会触发重新查找并返回自身以便链式调用:

大小写敏感(示例见 f-text-finder.ts):

const textFinder = await univerAPI.createTextFinderAsync('univer'); console.log(textFinder.findAll().map((c) => c.getA1Notation())); // 单元格内容为 'hello univer' / 'hello UNIVER' / 'HELLO UNIVER' / 'HELLO univer' // 默认忽略大小写 → A1, B1, C1, D1 await textFinder.matchCaseAsync(true); console.log(textFinder.findAll().map((c) => c.getA1Notation())); // 大小写敏感 → A1, D1

匹配整个单元格内容(示例见 f-text-finder.ts):

const textFinder = await univerAPI.createTextFinderAsync('hello univer'); // 默认部分匹配 → A1, B1, C1, D1 await textFinder.matchEntireCellAsync(true); // 整格匹配 → A1(其余单元格为 'hello univer 1/2/3',不命中)

按公式文本匹配(示例见 f-text-finder.ts):

// A1:D1 = ['sum', '1', '=SUM(2)', '3'] const textFinder = await univerAPI.createTextFinderAsync('sum'); console.log(textFinder.findAll().map((c) => c.getA1Notation())); // 默认按值匹配 → A1 await textFinder.matchFormulaTextAsync(true); console.log(textFinder.findAll().map((c) => c.getA1Notation())); // 按公式文本匹配 → A1, C1('=SUM(2)' 命中)

从实现看,matchCaseAsyncmatchEntireCellAsync分别更新caseSensitivematchesTheWholeCell状态,而matchFormulaTextAsync本质上是把findBy切换为FindBy.FORMULAFindBy.VALUE(见 f-text-finder.ts),与共享层FindBy枚举保持一致。

替换:单个替换与全部替换

替换当前命中项(示例见 f-text-finder.ts):

// B1:E1 = ['hello', 'hello', 'hello', 'hello'] const textFinder = await univerAPI.createTextFinderAsync('hello'); const replaced = await textFinder.replaceWithAsync('hello univer'); console.log(replaced); // true console.log(fRange.getValues()); // [['hello', 'hello', 'hello', 'hello univer']] —— 仅当前命中被替换

替换全部命中项(示例见 f-text-finder.ts):

// A1:D1 = ['hello', 'hello', 'hello', 'hello'] const textFinder = await univerAPI.createTextFinderAsync('hello'); const count = await textFinder.replaceAllWithAsync('hello univer'); console.log(count); // 4 console.log(fRange.getValues()); // [['hello univer', 'hello univer', 'hello univer', 'hello univer']]

实现上,替换操作会先通过_state.changeState({ replaceRevealed: true, replaceString: replaceText })写入替换文本,再委托FindReplaceModel.replace()/replaceAll()执行,底层经由表格的替换命令与SetRangeValuesCommand落到单元格数据(相关命令实现见 packages/sheets-find-replace/src/commands/commands/sheet-replace.command.ts)。

ensureCompleteAsync:切换工作表后的关键一步

FTextFinder的查找是异步完成的,且命中结果与当前工作表绑定。官方建议:只要切换了当前工作表,就应调用await textFinder.ensureCompleteAsync()重新完成查找,再继续遍历。官方示例(见 f-text-finder.ts):

const textFinder = await univerAPI.createTextFinderAsync('1'); const matchCells = textFinder.findAll(); matchCells.forEach((cell) => console.log(cell.getA1Notation())); const fWorkbook = univerAPI.getActiveWorkbook(); const sheets = fWorkbook.getSheets(); sheets[1]?.activate(); // 切换工作表 await textFinder.ensureCompleteAsync(); // 重新完成当前表查找 const matchCells2 = textFinder.findAll(); matchCells2.forEach((cell) => console.log(cell.getA1Notation()));

此外,findAll()在查找未完成时会返回空数组(见 f-text-finder.ts),而getCurrentMatch()会直接抛错提示 "Find operation is not completed.",因此按文档流程先await ensureCompleteAsync()是最稳妥的用法。

底层协作:工作表提供器与高亮

表格场景的查找替换由SheetsFindReplaceController(见 packages/sheets-find-replace/src/controllers/sheet-find-replace.controller.ts)负责编排。其职责包括:

  • IFindReplaceService注册工作表查找提供器(registerFindReplaceProvider),把"在哪个单元格范围里搜、命中后如何定位"的表格语义接入共享查找服务(见 sheet-find-replace.controller.ts);
  • 在单元格编辑器激活、公式编辑器聚焦等场景下自动关闭查找替换面板;
  • 通过ScrollToCellCommandSetSelectionsOperation等命令同步命中项的选中与滚动定位;
  • 使用SheetFindReplaceHighlightShape(见 packages/sheets-find-replace/src/views/shapes/find-replace-highlight.shape.ts)在渲染层绘制命中高亮。

因此,接入预设后不仅获得 UI 对话框与快捷键/菜单入口(@univerjs/find-replace中定义,相关控制器见 packages/find-replace/src/controllers/find-replace.controller.ts 与 packages/find-replace/src/menu/find-replace.menu.ts),还拥有与 Facade 编程一致的高亮与定位体验。

总结

@univerjs/preset-sheets-find-replace是 Univer 中"查找替换"功能域的一站式预设:

  • 接入成本极低pnpm add之后,在createUniverpresets数组中放入UniverSheetsFindReplacePreset(),并合并对应 locale 即可,样式自动生效;
  • 内部职责清晰:由UniverFindReplacePlugin(共享服务与 UI)+UniverSheetsFindReplacePlugin(表格语义扩展)组合而成,依赖渲染引擎、工作表内核与表格 UI 插件;
  • Facade 编程能力强univerAPI.createTextFinderAsync(text)返回FTextFinder,支持查找全部、前后遍历、大小写/整格/公式文本三种匹配模式,以及单个替换与全部替换,返回的命中项均为可继续操作的FRange
  • 版本一致是关键:与所有@univerjs/*包保持同一版本,并确保宿主满足 react / react-dom / rxjs 的 peer 依赖。

需要更深入的内容时,可继续研读 packages/find-replace(共享模型、服务与命令)与 packages/sheets-find-replace(表格控制器、高亮形状与 Facade 实现)这两个底层包,或参考 examples/src/preset-sheets-core/main.ts 的完整示例工程。

【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univer

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

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

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

立即咨询