[英]Vue - Storing a tree in Pinia
The question I'm going to ask is about Pinia, but really could just be generalized to any underlying store.我要问的问题是关于 Pinia 的,但实际上可以推广到任何基础商店。
I have a Vue + Pinia application in which I want to be able to store a tree.我有一个 Vue + Pinia 应用程序,我希望能够在其中存储一棵树。 The tree is made up of objects of type
Node
.该树由
Node
类型的对象组成。 I need to store exactly one tree at a time, and I don't care about the root (we can imagine it's there, but what I care about is the root's children, their children, and so on).我需要一次只存储一棵树,我不关心根(我们可以想象它在那里,但我关心的是根的孩子,他们的孩子,等等)。
I want to support the following operations:我想支持以下操作:
Here's something I have thought of doing:这是我想做的事情:
Node
s containing the top level nodes.Node
数组。 Let's call it topLevelNodes
topLevelNodes
nodeIdToChildren
, which maps the id of a node to an array of Node
s that are its childrennodeIdToChildren
,它将节点的 id 映射到作为其子节点的Node
数组I would initially fetch the top level nodes, filling the array topLevelNodes
.我最初会获取顶级节点,填充数组
topLevelNodes
。
For each node that needs to know its children, fetch them and put them in nodeIdToChildren
under the parent id as key.对于每个需要知道其子节点的节点,获取它们并将它们作为键放入父 ID 下的
nodeIdToChildren
中。
An advantage of this approach is that it's easy to add, delete, and move around nodes: just touch the relevant entries in the mapping.这种方法的一个优点是很容易添加、删除和移动节点:只需触摸映射中的相关条目。 The biggest drawback is that it's much less efficient to just find a node, regardless of its position. Say I want to edit node with id
xyz
, not knowing whose child it is.最大的缺点是只找到一个节点的效率要低得多,不管它的 position。假设我想编辑 id 为
xyz
的节点,不知道它是谁的孩子。
I could create a getter that flattens all the values in the mapping object together with the values in the top level nodes array, but I'm not sure about efficiency of that.我可以创建一个吸气剂,将映射 object 中的所有值与顶级节点数组中的值一起展平,但我不确定其效率。
Are there any better ways of doing this?有没有更好的方法来做到这一点?
The most efficient way is to store all nodes in a single array and in the children/parents array of a node you only place ids of other nodes.最有效的方法是将所有节点存储在一个数组中,而在一个节点的子/父数组中,您只放置其他节点的 ID。
parents
, but it returns the ids of all items which currently contain the current item's id in their children array.parents
,但它返回所有项目的 ids,这些项目当前在他们的 children 数组中包含当前项目的 id。 Similarly, if you store parents, the children will be a computed. Notes :注意事项:
getItemDetails
))getItemDetails
))children
array of ids, add its ID to the new parent's children
. The child itself is not affected, except the value of its parents
computed changes. If you're storing parents
, - and children
is computed - you need to change the child's parents
array. And, of course, each of the old and new parent's children
computed will change when you perform this change). children
ID 数组中删除它的 ID,将它的 ID 添加到新父母的children
中。孩子本身不受影响,除了值它的parents
计算的变化。如果你正在存储parents
, - 并且计算了children
- 你需要改变孩子的parents
数组。当然,当你执行这个改变时,每个新旧父母的children
计算都会改变) . You can see a working demo here .您可以在此处查看工作演示。 Sorry about the styles, I just tweaked something from a different sandbox.
对不起 styles,我只是从不同的沙盒中调整了一些东西。 But you get the basic implementations of both
<TreeView />
and <TableView />
.但是你得到了
<TreeView />
和<TableView />
的基本实现。 This one has 1k nodes but should have no issues up to 5k nodes.这个有 1k 个节点,但最多 5k 个节点应该没有问题。 Above that, you need to be careful what you render and when.
除此之外,您需要注意渲染的内容和时间。
This is not specified to Vue/Pinia so I just write the general idea, you can choose the way to implement it.这不是Vue/Pinia特有的,所以我只是写了大概的思路,你可以选择实现的方式。
You will store your tree on a flat map.您将把您的树存放在一个平面 map 上。
const tree = new Map()
Each item will be a node
and the key is the nodeId
.每个项目都是一个
node
,键是nodeId
。 Each node will contain these properties:每个节点将包含以下属性:
{
id: "the node id",
parentId: "id of the parent, null if it is the root node",
childIds: "array of the id of its child",
content: "content of the node, whatever you want"
}
Let's go through each operator you want:让我们通过您想要的每个运营商 go:
// difficulty: easy
const rootNode = {
id: "nodeId"
parentId: null,
childIds: [...],
content: "..."
}
tree.set(nodeId, rootNode)
// difficulty: easy
// add the child first
const childNode = {
id: "nodeId"
parentId: "parentId",
childIds: [...],
content: "..."
}
tree.set(nodeId, childNode)
// add the child id to the parent node
const parentNode = tree.get(parentId)
parentNode.childIds.push(childNode.id)
// set the parent back to your tree
tree.set(parentNode.id, parentNode)
// modify a node.
// difficulty: easy
const node = tree.get(nodeId)
// ... make the modification
// set it back to the tree
tree.set(nodeId, node)
// delete a node.
// difficulty: medium
// retrieve the node first
const node = tree.get(nodeId)
// delete it
tree.delete(nodeId)
// delete all of its children
// you need a recursive delete function here to go through all of the node child and child of child and so on
tree.childIds.forEach(recursiveDelete)
// don't forget to delete the nodeId from its parent node. It's easy
...
// moving around the tree is quite easy, you just need to follow the `parentId` and `childIds`
// changing a node's parent (same level)
// difficulty: easy
// you just need to change the parentId of the node. And modify the childIds of its old and new parent
// changing a node level, moving its children accordingly
// difficulty: easy
// same as changing a node parent above. Its children will move accordingly
// changing a node level to be a child of one of its children
// difficulty: hard
// get the node
const node = tree.get(nodeId)
// go through its children and update the parentId of each to the node.parentId (moving its children to be the direct child of its parent)
node.childIds.forEach((childId)=> updateParentId(childId, node.parentId))
// set the node parentId to the new one
node.parentId = newParentId
// set new childIds for the node if you want
node.childIds = [...]
// don't forget to set it back on the tree
tree.set(node.id, node)
// There is no problem at all. You just need to load from the root
Pros and Cons优点和缺点
Pros:优点:
Cons缺点
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.