Under the Hood.zh_CN - dsblank/gramps-connect GitHub Wiki
🌐 English · Deutsch · Español · Français · Nederlands
底层细节
一些放不进架构叙述主线的实现说明——如果您正在调试某个问题、审查应用在哪里存储了什么,或者想知道它为什么会有某种行为,这些细节值得了解。
浏览器存储:sessionStorage 与 localStorage
两者的作用范围都是浏览器配置文件 + 源(origin),而不是当前登录的用户。如果用户 A 退出登录、用户 B 在同一个浏览器上登录(同一台电脑、同一个浏览器配置文件),用户 B 的标签页会从该源的存储中已有的内容开始——除非应用显式地清除它或为它加上命名空间。Gramps Connect 正是沿着这条界线,把客户端存储分到这两个 API 中:
sessionStorage(app/src/auth/auth.ts)保存访问令牌、刷新令牌和用户名。logout() 会显式清除这三项,而且 sessionStorage 本身在标签页关闭后不会保留——因此新的登录总是从干净的状态开始,这里不会在用户之间泄露任何东西。
localStorage 保存界面偏好设置,而且——与认证令牌不同——退出登录时都不会清除:
| 键 | 作用范围 | 文件 |
|---|---|---|
gramps-connect_home_person |
按家谱(以 getTreeId() 为键) |
app/src/store/homePersonPreference.ts |
gramps-connect_column_widths |
全局 | app/src/store/columnWidths.ts |
gramps-connect_tree_manual_expand |
全局 | app/src/store/treeExpandPreference.ts |
gramps-connect_aside_widths |
全局 | app/src/App.tsx |
gramps-connect:map-viewport |
全局 | app/src/components/visuals/MapCanvas.tsx |
gramps-connect_browser_notifications_enabled |
全局 | app/src/store/browserNotifications.ts |
这些键都没有按用户划分命名空间,只有 home_person 按家谱划分。实际影响是:在一台共用的电脑上,下一个登录的人(对于全局键而言,即使登录的是另一棵家谱)会继承上一个用户的列宽、窗格大小、地图视口和通知开关——如果是同一棵家谱,还会继承其主人翁选择。不会暴露任何敏感信息(这里没有家谱数据或凭据,只有显示偏好),但这是用户可能注意到并询问的真实行为。
如果将来需要按用户隔离,修复方法与 homePersonPreference.ts 已用于按家谱划分的模式相同——用用户 id(或用户名)代替家谱 id 作为存储对象的键,或在家谱 id 之外再加上它。
相关/详情窗格:再次访问时立即重绘
右上方(以及底部“Reference detail”)窗格的单条记录获取(fetchObjectExtended,app/src/store/objectDetail.ts)由一个小型内存缓存支撑,采用 stale-while-revalidate 策略,以视图 + handle 为键,最多 50 条(LRU 淘汰)。离开一条记录再回来时,会立即重绘最后一次已知的详情,而不是显示加载动画——底层列表视图(DataTable)早已通过其持久化的 ViewStore/SQLite 缓存做到这一点(见架构);这为详情窗格补上了同样的缺口,它以前每次挂载都会从头重新获取。
它并不是“跳过请求”意义上的真正缓存:每次挂载仍然和以前一样调用 fetchObjectExtended,并用新结果覆盖缓存条目。与上面的 localStorage 键一样,它是模块级缓存——退出登录或切换家谱时不会清除。
每当实时同步报告家谱有任何变化时,这个窗格也会重新获取,而不仅仅是它自己的(视图, handle)发生变化时。实时同步的常规路径只会在变化与某个 ViewStore 自己的表/handle 匹配时才增加其修订号,这对列表视图来说足够精确,但在这里不够:对嵌入在本记录已解析引用中的其他记录所做的编辑(某条摘录的文本、通过 extend=all/反向链接带入的某个事件的日期)否则不会触发重新获取,于是会一直过时,直到窗格重新挂载。在每次家谱变化时都重新获取,是用一点额外的轮询流量换取正确性,而不必重新推导哪些 handle 被嵌入——这种结构因对象类型而异(见数据模型与编辑)。