文档教程List 和 Movable List

List 与 MovableList

Loro 提供两种列表:ListMovableListList 支持插入、删除;MovableList 额外支持 setmove

使用插入/删除可在 List 中模拟 set/move,但在并发场景会失效:同一元素被同时 set 或 move 时,模拟方案会删除原元素并插入两个新元素,结果不符合预期。

MovableList 可以正确处理这些情况,不过需要额外开销:仅执行插入/删除时,它在编解码上大约慢 80%,内存开销约高 50%。

两者都基于 Fugue 实现最大非交错性;MovableList 的移动能力额外使用 Moving Elements in List CRDTs 算法。

基本用法

List

const  = new ();
.("1");
const  = .("list");
.(0);
.(1);
.(2);
const :  = .({ : "snapshot" });
 
const  = .();
.("2");
const  = .("list");
{
  // docA、docB 并发修改索引 2
  .(2, 1);
  .(2, 9);
  (.()).({ : [0, 1, 9] });
  .(2, 1);
  .(2, 8);
  (.()).({ : [0, 1, 8] });
}
 
{
  .(.({ : "update", : .() }));
  .(.({ : "update", : .() }));
}
 
(.()).({ : [0, 1, 8, 9] });
(.()).({ : [0, 1, 8, 9] });

MovableList

const  = new ();
.("1");
const  = .("list");
.(0);
.(1);
.(2);
const :  = .({ : "snapshot" });
 
const  = .();
.("2");
const  = .("list");
{
  .(2, 8);
  (.()).({ : [0, 1, 8] });
  .(2, 9);
  (.()).({ : [0, 1, 9] });
}
 
{
  .(.({ : "update", : .() }));
  .(.({ : "update", : .() }));
}
 
// 因 docB 的 peerId 更大,最终收敛为 [0, 1, 9]
(.()).({ : [0, 1, 9] });
(.()).({ : [0, 1, 9] });
 
{
  .(0, 2);
  .(0, 1);
  (.()).({ : [1, 9, 0] });
  (.()).({ : [1, 0, 9] });
}
 
{
  .(.({ : "update", : .() }));
  .(.({ : "update", : .() }));
}
 
(.()).({ : [1, 0, 9] });
(.()).({ : [1, 0, 9] });

List 上的光标

List 的光标会随列表变动:前方插入 → 光标右移;前方删除 → 左移;后方改动则不受影响。可用于稳定表示段落等结构的选区。

const  = new ();
.("1");
const  = .("list");
.("Hello");
.("World");
const  = .(1)!;
.(.()); // { peer: "1", counter: 1 }
 
const :  = .();
const :  = .({ : "snapshot" });
 
const  = new ();
.("2");
const  = .("list");
.();
.(0, "Foo");
.(.()); // { list: ["Foo", "Hello", "World"] }
const  = .();
{
  const  = .();
  .(.); // 2
}
.(1, "Bar");
.(.()); // { list: ["Foo", "Bar", "Hello", "World"] }
{
  const  = .();
  .(.); // 3
}
.(3, 1);
.(.()); // { list: ["Foo", "Bar", "Hello"] }
{
  const  = .();
  .(.); // 3
  .(.); // { peer: "2", counter: 1 }
  const :  = .!;
  .(.()); // undefined(位于列表末尾)
  .(.()); // 1(位于右端)
  const  = .();
  .(.); // 3
  .(.); // undefined
}