<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>数据一致性 | 锤子前端</title><description>Now vibe coding, so learning hammer FE ?</description><link>https://channel.meo-ai.com</link><item><title>《Notion如何用CRDT解决并发编辑冲突》</title><link>https://channel.meo-ai.com/posts/711</link><guid isPermaLink="true">https://channel.meo-ai.com/posts/711</guid><pubDate>Thu, 24 Sep 2026 12:31:04 GMT</pubDate><content:encoded>&lt;span class=&quot;post-title-source&quot;&gt;《Notion如何用CRDT解决并发编辑冲突》&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;标签：&lt;br /&gt;&lt;a href=&quot;/search/result?q=%23%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F&quot; title=&quot;#分布式系统&quot;&gt;#分布式系统&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E5%8D%8F%E5%90%8C%E7%BC%96%E8%BE%91&quot; title=&quot;#协同编辑&quot;&gt;#协同编辑&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23CRDT%E7%AE%97%E6%B3%95&quot; title=&quot;#CRDT算法&quot;&gt;#CRDT算法&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7&quot; title=&quot;#数据一致性&quot;&gt;#数据一致性&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;总结：&lt;br /&gt;&lt;br /&gt;文章要点：&lt;br /&gt;&lt;br /&gt;1. Notion在2025年重新设计了底层文本编辑系统，从&quot;最后写入获胜&quot;(LWW)模式转向基于CRDT的并发编辑方案，解决了多人同时编辑同一区块时的数据丢失问题。&lt;br /&gt;&lt;br /&gt;2. CRDT(无冲突复制数据类型)允许多个客户端保留本地数据副本，并以确定性方式合并同时发生的更改，确保不会丢失任何人的修改。&lt;br /&gt;&lt;br /&gt;3. Notion使用的CRDT基于经典的RGA(可复制增长数组)序列CRDT，这是一个树形数据结构，每个字符都有唯一且稳定的ID，插入和删除操作都引用这些ID。&lt;br /&gt;&lt;br /&gt;4. 删除操作采用&quot;墓碑&quot;机制——标记项目为已删除但保留在树中，因为可能有在途或离线操作依赖于已删除字符的ID。&lt;br /&gt;&lt;br /&gt;5. 为提升存储效率，Notion将连续字符分组，为整组字符分配一个ID并存储运行长度，而不是为每个字符单独分配ID。&lt;br /&gt;&lt;br /&gt;6. 支持富文本格式需要解决注释冲突，Notion引入了基于Peritext算法的操作，支持可扩展和不可扩展的注释类型(如粗体vs超链接)。&lt;br /&gt;&lt;br /&gt;7. Notion的区块模型带来了独特挑战：当用户按下回车键分割区块时，需要处理文本在区块间移动时的并发编辑问题。&lt;br /&gt;&lt;br /&gt;8. 为解决这一问题，Notion引入了&quot;文本切片&quot;概念——每个区块的文本项属于一个文本切片，分割区块时切片也会相应分割并移动到新区块。&lt;br /&gt;&lt;br /&gt;9. 每个文本项ID由会话ID和Lamport时钟组成，确保ID的唯一性；指向同一原点的项目按逻辑时间戳排序，会话ID作为平局决胜因素。&lt;br /&gt;&lt;br /&gt;URL：&lt;br /&gt;&lt;a href=&quot;https://www.notion.com/blog/how-notion-handles-concurrent-editing-with-crdts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;https://www.notion.com/blog/how-notion-handles-concurrent-editing-with-crdts&quot;&gt;https://www.notion.com/blog/how-notion-handles-concurrent-editing-with-crdts&lt;/a&gt;&lt;a class=&quot;tgme_widget_message_link_preview&quot; href=&quot;https://notion.com/blog/how-notion-handles-concurrent-editing-with-crdts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;Building a CRDT-based rich-text system that merges concurrent edits across Notion blocks—without losing anyone’s work&quot;&gt;
  
  &lt;div class=&quot;link_preview_site_name accent_color&quot;&gt;Notion&lt;/div&gt;
  &lt;img class=&quot;link_preview_image&quot; alt=&quot;How Notion handles concurrent editing with CRDTs&quot; src=&quot;/static/https://cdn4.telesco.pe/file/q84q8yLEOOmLTc0ofDVh85lGSkkH34LEWNUWMrHuBfa8DMVFJ70uPJOOE91WEi10t1DrhmFYqsXCh1NfU5hDlqL6-yqn4KkduyVHu_oxyCo84ZKtV4pGmbLs6tebPyW48imzpJhD0zBjXh0zjXYpH24aeEqdBxnY-baWgrjztdYEW1OCTR6C8sUFD3sE_7GA_YIMdh1-n2XV41pkqbnZCulJHntHA5-HoW6BP4SjHzBORFYzALteREU5YTaZQdwxavRRotbEdDRUxd9Gt1HRzT1Xk1zPBdCs5HUxAupip30-MXf3AHX9kObTW78Rco26_apNkgmvHdx0OvySIBASAw.jpg&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;eager&quot; /&gt;
  &lt;div class=&quot;link_preview_title&quot;&gt;How Notion handles concurrent editing with CRDTs&lt;/div&gt;
  &lt;div class=&quot;link_preview_description&quot;&gt;Building a CRDT-based rich-text system that merges concurrent edits across Notion blocks—without losing anyone’s work&lt;/div&gt;
&lt;/a&gt;</content:encoded></item><item><title>《Microcharts：一句话里就能塞下的React微图表库》</title><link>https://channel.meo-ai.com/posts/688</link><guid isPermaLink="true">https://channel.meo-ai.com/posts/688</guid><pubDate>Sat, 01 Aug 2026 09:20:15 GMT</pubDate><content:encoded>&lt;span class=&quot;post-title-source&quot;&gt;《Microcharts：一句话里就能塞下的React微图表库》&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;标签：&lt;a href=&quot;/search/result?q=%23%E5%89%8D%E7%AB%AF&quot; title=&quot;#前端&quot;&gt;#前端&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23React&quot; title=&quot;#React&quot;&gt;#React&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%8C%96&quot; title=&quot;#数据可视化&quot;&gt;#数据可视化&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E5%9B%BE%E8%A1%A8%E5%BA%93&quot; title=&quot;#图表库&quot;&gt;#图表库&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96&quot; title=&quot;#性能优化&quot;&gt;#性能优化&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%97%A0%E9%9A%9C%E7%A2%8D&quot; title=&quot;#无障碍&quot;&gt;#无障碍&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23ServerComponents&quot; title=&quot;#ServerComponents&quot;&gt;#ServerComponents&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;总结：&lt;br /&gt;Microcharts是一个专为React设计的微图表库，提供106种图表类型，单个图表gzip后仅2.18-7KB（中位数5.25KB），零依赖。它主打&quot;词级图表&quot;理念——图表小到可以嵌入句子、表格单元格和KPI卡片中，无需坐标轴和图例，由上下文文字承载含义。支持服务端组件静态渲染（0KB客户端JS）、自动生成无障碍alt文本、单色系主题系统，并能在错误数据下优雅降级。&lt;br /&gt;&lt;br /&gt;文章要点：&lt;br /&gt;1. 小到离谱：中位数图表仅5.25KB gzip，最大7KB，零运行时依赖，React只是peer dependency，比通用图表库（如Recharts 106KB共享内核）轻了整整101KB&lt;br /&gt;2. 一句话装得下：图表设计为&quot;词级尺寸&quot;，可以直接嵌入正文、表格单元格、KPI卡片甚至打印报告，读者不用离开句子就能理解数字&lt;br /&gt;3. 106种图表统一API：传一个data数组就搞定，domain、color、title在每个图表里含义一致，TypeScript类型完备，编辑器能自动补全&lt;br /&gt;4. 静态渲染零负担：在Server Component里渲染时完全不产生客户端JS，hydrate成本为0，对性能敏感场景极其友好&lt;br /&gt;5. 坏数据也能优雅处理：NaN、空数组、单一数值都不会报错，会自动渲染成点、空白或水平线，不用写try/catch兜底&lt;br /&gt;6. 无障碍开箱即用：自动生成alt文本（如&quot;趋势上升27%，范围128到163&quot;），每个图表一个tab焦点，支持方向键浏览，色盲安全配色和RTL都内置&lt;br /&gt;7. 单色系主题系统：给一个accent主色就能自动推导出完整配色（包括正负色、分类色、暗色模式），整个页面一键换肤&lt;br /&gt;&lt;br /&gt;URL：&lt;a href=&quot;https://microcharts.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;https://microcharts.dev/&quot;&gt;https://microcharts.dev/&lt;/a&gt;&lt;a class=&quot;tgme_widget_message_link_preview&quot; href=&quot;https://microcharts.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;Word-sized React charts — tiny sparklines, micro charts &amp;amp; inline SVG trend lines. Zero runtime deps, ~2–7 kB interactive · ~1–4 kB static, accessible by default, RSC-safe. Sit in a sentence, table cell, KPI card, or AI reply.&quot;&gt;
  
  &lt;div class=&quot;link_preview_site_name accent_color&quot;&gt;microcharts&lt;/div&gt;
  &lt;img class=&quot;link_preview_image&quot; alt=&quot;microcharts — Word-sized charts for React.&quot; src=&quot;/static/https://cdn4.telesco.pe/file/HL18sZBYJ6CmXzfv4FiWPiCXLBU9IBJLeT2-o3a8_fvGEka3eFREc06UBbL3YE3_HEyaiShzH4bZ0qgcAuh_QGGoHiZraOY1QiXTwFno5Ts5hgTfpph-wxi5-ETIdnLizAKWXjxaHGFpxsh-8a1bFrHytH-9_TQ5P9xB_cIvzokDamofEFCg03eK-Ay8zVNrTmNZCWZ9RH2Z7cTaFvsakImItcQNQGMWvUauWUBqEshnp2TubuiPUebSqvV77UQxPcsA8NhOzgld7JB-GmY4ibFVO9zd6hEgaLwrfS3MtyAbJ7gjM9XUdmHQiqU5-rX1thQU1laoQaLMSvc087RLTw.jpg&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;eager&quot; /&gt;
  &lt;div class=&quot;link_preview_title&quot;&gt;microcharts — Word-sized charts for React.&lt;/div&gt;
  &lt;div class=&quot;link_preview_description&quot;&gt;Word-sized React charts — tiny sparklines, micro charts &amp;amp; inline SVG trend lines. Zero runtime deps, ~2–7 kB interactive · ~1–4 kB static, accessible by default, RSC-safe. Sit in a sentence, table cell, KPI card, or AI reply.&lt;/div&gt;
&lt;/a&gt;</content:encoded></item><item><title>《利用浏览器Canvas进行数据压缩》</title><link>https://channel.meo-ai.com/posts/565</link><guid isPermaLink="true">https://channel.meo-ai.com/posts/565</guid><pubDate>Fri, 27 Feb 2026 02:29:12 GMT</pubDate><content:encoded>&lt;span class=&quot;post-title-source&quot;&gt;《利用浏览器Canvas进行数据压缩》&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;标签：&lt;a href=&quot;/search/result?q=%23%E5%89%8D%E7%AB%AF&quot; title=&quot;#前端&quot;&gt;#前端&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23JavaScript&quot; title=&quot;#JavaScript&quot;&gt;#JavaScript&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23CanvasAPI&quot; title=&quot;#CanvasAPI&quot;&gt;#CanvasAPI&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%95%B0%E6%8D%AE%E5%8E%8B%E7%BC%A9&quot; title=&quot;#数据压缩&quot;&gt;#数据压缩&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23PNG%E7%BC%96%E7%A0%81&quot; title=&quot;#PNG编码&quot;&gt;#PNG编码&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7&quot; title=&quot;#浏览器兼容性&quot;&gt;#浏览器兼容性&lt;/a&gt; &lt;a href=&quot;/search/result?q=%23SPA&quot; title=&quot;#SPA&quot;&gt;#SPA&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;总结：本文介绍了一种利用浏览器Canvas API将任意数据压缩为PNG图像格式的技术方案。通过将字节数据编码为像素颜色值并生成PNG图像，可以间接调用浏览器内置的压缩算法，实现无需外部依赖的数据压缩。该方法特别适用于需要在旧版浏览器中压缩数据、或需要将SPA状态序列化到URL中的场景，提供了Compression Streams API不可用时的替代方案。&lt;br /&gt;&lt;br /&gt;文章要点：&lt;br /&gt;- 背景需求：在静态网站和SPA中，有时需要将状态数据序列化到URL hash中，因此需要前端数据压缩方案；虽然2023年5月后Compression Streams API已普及，但旧版浏览器仍需替代方案&lt;br /&gt;- 核心原理：浏览器内置了优化的压缩库用于HTTP请求和图片处理，通过将数据编码为PNG像素数据，可间接利用浏览器的无损压缩能力&lt;br /&gt;- 技术实现：将Uint8Array数据按RGB通道编码到Canvas像素中（首字节存储最后一像素的有效字节数），Alpha通道固定为255以确保跨浏览器一致性，最终通过toDataURL(&quot;image/png&quot;)获取base64编码的压缩数据&lt;br /&gt;- 解压流程：异步加载生成的PNG图片，读取像素数据后过滤Alpha通道，根据首字节指示的有效长度提取原始字节数据&lt;br /&gt;- 方案特点：即使考虑PNG格式开销，压缩后的数据通常仍小于原始数据；完全基于浏览器原生API，无需外部库依赖&lt;br /&gt;- 应用场景：旧浏览器兼容性支持、URL状态序列化、纯前端数据压缩需求&lt;br /&gt;&lt;br /&gt;文章URL：&lt;a href=&quot;https://jstrieb.github.io/posts/canvas-compress/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;https://jstrieb.github.io/posts/canvas-compress/&quot;&gt;https://jstrieb.github.io/posts/canvas-compress/&lt;/a&gt;&lt;a class=&quot;tgme_widget_message_link_preview&quot; href=&quot;https://jstrieb.github.io/posts/canvas-compress&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;Old browsers don’t have JavaScript compression APIs, so we store data in pixel color channels and export to PNG to compress data.&quot;&gt;
  
  &lt;div class=&quot;link_preview_site_name accent_color&quot;&gt;jstrieb.github.io&lt;/div&gt;
  
  &lt;div class=&quot;link_preview_title&quot;&gt;Using the Browser’s &amp;lt;canvas&amp;gt; for Data Compression&lt;/div&gt;
  &lt;div class=&quot;link_preview_description&quot;&gt;Old browsers don’t have JavaScript compression APIs, so we store data in pixel color channels and export to PNG to compress data.&lt;/div&gt;
&lt;/a&gt;</content:encoded></item></channel></rss>