文章目录
文章目录
本文目录
本文目录

低代码表单切换附件,旧文档实例由谁结束?

石墨办公 + AI

以 PCF 公开契约与石墨办公 JS SDK 固定源码为例,设计容器复用时的实例、回调、异步晚到和关闭验收,区分隐藏、移除与保存。

低代码表单中嵌入在线文档,第一次打开成功之后,还要验证一条容易遗漏的路径:打开记录 A 的附件,关闭弹窗,再打开记录 B,页面复用了原来的容器。此时谁结束 A 的编辑器实例,谁接收迟到的保存通知,谁保证 B 的状态没有被 A 改写?这些责任应由接入组件明确管理,不能仅凭弹窗消失就认定旧实例已经结束。

本文面向负责企业业务系统接入的开发者,以 Power Apps component framework(PCF)的 StandardControl 公开契约和石墨办公文档中台私有部署(以下简称文档中台)JS SDK 固定源码为例,给出组件生命周期的验收设计。场景限于 model-driven app 的设计讨论;没有在 Power Apps 租户安装或测试文档中台组件,也不表示两者已完成适配。A、B 和实例代次均为假设标记。

把“更新页面”与“换一份文件”拆开

PCF 的 init 接收用于渲染的 div 容器;官方同时说明,dataset 值不能在这里初始化,应在 updateView 中处理。得到容器,并不意味着业务附件身份已经准备好。

updateView 的触发范围更宽:字段、dataset、容器尺寸、离线状态、label、visible 等属性变化都可能触发;数据尚未准备好时,传入值还可能为 null。因此,不宜把每次 updateView 都实现为“重新 connect 一次”。同一文件只是宽度改变,不应因这次更新意外增加第二个文档实例。

接入组件可以维护一份仅供本次页面运行使用的归属记录:业务记录标识、文件标识、实例代次、SDK 句柄、该实例注册的回调,以及当前观察到的保存状态。它用于判断“这个结果属于谁”,不是权限凭据,也不能替代服务端授权。数据暂空时,不应沿用上一个文件身份启动新编辑器;应保留明确的等待状态,按业务规则处理已有实例。

平台给出的变化组件先判断什么应留下的验收观察
容器出现,文件值暂空业务文件身份及打开条件是否齐备是否误打开上一条记录;何时才发起本次初始化
A 的尺寸或 label 变化文件身份是否仍为 A是否重复创建 iframe、重复注册保存回调
业务记录切到 BA 的关闭条件与实例归属如何结束B 的句柄和状态是否始终关联 B;A 的晚到结果去了哪里
visible 改变只是隐藏,还是组件真的移除原实例是否保留;重新显示时究竟打开哪份文件

用具体对象接住 A 的退出

本文核对文档中台官方 shimo-js-sdk 固定提交:package.json 标注 2.0.4,生成文档仍带 v2.0.2 标签,因此下述行为以该提交源码交叉核验。这不是“最新发行版”的判断,也不能代替项目使用的服务端版本。README 明确,该库对应文档中台 SDK 2.0 产品,不能用于 shimo.im SaaS 产品。

该提交的 connect 实现创建 OfficeSDK,等待 init 后返回实例。文件身份通过初始化参数传入;本文核对的公开方法中未见同实例 switchFile 或 setFileId 契约,不能通过修改私有属性模拟换文档,也不能把 setCredentials 当作换文件方法。其他版本如有对应能力,应按其正式接口重新确认。

组件拿到 SDK 后,可通过 getEditor() 获取编辑器对象。编辑器类型定义提供 on/off。注册 A 的保存回调时,应保存原函数引用,并让它关联 A 的身份和代次;退出 A 时,按这个引用解除本地事件监听;这不表示取消服务端事件订阅。若回调只读取一个不断变化的“当前记录”,A 的通知就可能被记到 B 名下。

同样,清理 A 时应操作 A 的句柄,不能在迟到回调中读取一个已被 B 覆盖的全局 sdk 变量,再调用 disconnect。否则清理代码可能结束的是新实例。接入方自己创建的定时器、订阅和异步工作,也要分别记录归属,不能认为 SDK 会替业务代码全部释放。

A 的初始化晚到,标记失效仍不等于取消

把下面的顺序作为一条独立用例:A 发起 connect,但尚未返回;用户请求切到 B;A 随后才完成初始化。组件需要知道 A 的结果已失效,不能让它覆盖 B 的句柄、标题或保存提示。

给每轮打开分配递增的代次,是一种业务层设计方法。采用异步结果之前,同时核对记录、文件和代次;迟到的 A 结果不能被认作 B。若之后取得了 A 的旧句柄,可以针对它安排清理。这里的“代次”是接入组件自定义的状态,不是文档中台 SDK 参数,也不是文件版本或并发写入保护。

这个判断不能取消尚未完成的 connect。所核实现会在初始化过程中创建并挂载 iframe;等 Promise 返回后再丢弃旧结果,不能证明旧 iframe 从未短暂出现,也不能证明初始化已被中止。本文未核得可直接用于该流程的取消契约,因此不提供一段声称彻底消除竞态的示例代码。

如果项目要求“切换之后,旧文件绝不能再出现在可见区域”,就必须单独验证初始化期间的隔离与退出方式。可先评估限制连续切换或为每次打开分配独立宿主容器的方案,再结合平台允许的组件结构验证;这些都是待验证的实现选择。没有可证实的退出路径时,应把这条要求保留为未满足,不能用代次检查替它签字。

容器复用时分别追踪三个阶段:A 发起初始化并绑定代次,切换请求使 A 的结果失效,A 晚返回时不能接管 B。失效标记不取消初始化;隐藏不等于移除;断开不等于保存完成。
原创责任示意:组件决定结果归属,实际初始化与清理仍按接口执行。图中没有产品界面或实测结果。

关闭策略要早于 destroy 决定

PCF 的 destroy 在组件将从 DOM 树移除时调用,用于清理,返回 void。弹窗隐藏是否移除组件,取决于具体页面实现;不能从“关了弹窗”推导 destroy 已执行,也不应把需要等待的异步保存流程留到 destroy 才开始。

该提交的 disconnect 实现同步移除所挂载 iframe,并移除相应的 window message 与视口 resize 监听。它没有等待保存的步骤。调用返回只能说明这段清理方法返回,不能据此显示“内容已保存”。

编辑器的 saveStatusChanged 类型允许 status 为 saving、saved、error,字段本身也可缺失。接入组件应把没收到状态或字段缺失保留为“未知”。观察到 saved,也不能直接当作业务审批提交、所有外部存储或归档完成;这些过程的区别可结合业务系统与文档 SDK 的接入边界继续核对。

关闭动作由谁发起,就应在它仍能控制页面时处理保存状态:只读场景、保存中、保存错误与未知状态分别采取约定策略。若选择等待,需定义超时后的提示和处理;若允许带未决状态退出,必须告知用户并保留未决记录。不能把“关闭成功”作为保存成功的替代证据。

按六条路径验收,而不是只截一张打开成功图

先记录目标 Power Apps 应用类型及实际运行版本、自定义组件版本、JS SDK 版本、文档服务版本和浏览器。PCF manifest 的 version 表示组件自身版本,不能拿它或官方文档更新日期代替平台 build。下面只列待执行用例及判断条件,没有填写任何实测通过结果。

  1. 空值到有效文件。让容器先出现、文件值后到达。记录初始化次数与目标文件,检查是否出现上一条记录的附件。
  2. 同文件反复更新。保持 A 不变,改变尺寸、label 或可见性。记录实际触发与实例数,确认没有因无关更新重复注册回调。
  3. A 初始化延迟后切 B。在隔离环境控制初始化时序,记录发起和返回顺序、句柄归属、可见 iframe,以及晚到回调修改了哪个状态。业务层拒绝旧结果与旧 iframe 不出现,分开判断。
  4. 隐藏后再显示。记录是否发生 destroy,检查保留或重建策略是否符合约定;再用不同记录重新打开,核对可见文件身份。
  5. 保存中、错误或未知时关闭。分别观察提示、等待与超时处置,检查是否错误显示“已保存”;重新打开并核对实际内容,不能只看状态灯。
  6. 真正移除组件。检查 A 的监听是否解绑、iframe 是否移除、业务订阅是否结束;再打开 B,验证旧清理没有结束新实例。一次用例通过不等于已经证明不存在内存泄漏。

每条用例至少保留时间顺序、业务记录与文件、代次、触发方法、句柄及回调归属、可见内容和实际结果。日志中不写正文、token 或签名;用于关联的标识也应按企业规则脱敏。发现错误时,把“旧结果被错误采用”“旧界面仍可见”“保存状态未确认”分别记录,三者需要的修复可能不同。

确认平台约束,再讨论嵌入方案

PCF 的官方限制要求组件使用平台库或把外部代码打入主 bundle,要求组件不要使用 localStorage、sessionStorage 存储数据;canvas app 中不支持组件自定义鉴权。因此,不能把任意网页的 CDN 加载示例直接当作 PCF 安装步骤,更不能把本文 model-driven 的设计讨论外推成 canvas 适配结论。

iframe/CSP、认证、浏览器限制与目标网络仍需在实际环境核对。若准备评估文档中台接入,可携带平台及组件版本、允许的扩展方式、一条 A→关闭→B 的业务路径和上述异常用例,预约接入演示。需要确认的是这条路径中每个实例由谁建立、结束和验收,而不只是编辑器能否在一个空页面上出现。也可以先看私有化部署方案,了解部署形态与交付范围。