Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

代码优化之路 #5

Open
zyascend opened this issue Jul 12, 2022 · 8 comments
Open

代码优化之路 #5

zyascend opened this issue Jul 12, 2022 · 8 comments
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@zyascend
Copy link
Owner

zyascend commented Jul 12, 2022

记录项目重构优化过程中的重大调整。

@zyascend zyascend self-assigned this Jul 12, 2022
@zyascend zyascend added documentation Improvements or additions to documentation enhancement New feature or request labels Jul 12, 2022
@zyascend
Copy link
Owner Author

zyascend commented Jul 12, 2022

【问题】
store中直接发起http请求, 使store看起来很混乱。
【分析】
所有store的职责只有对状态的维护,除此之外不应该做任何事。
【解决】
将所有http请求抽取到统一的工具文件,根据不同功能模块暴露相关方法。store只是调用相关方法。
see: b2a94c9

@zyascend
Copy link
Owner Author

zyascend commented Jul 12, 2022

【问题】
导图相关尺寸位置计算依赖于store中保存的svg的ref。相关问题:#4
【分析】
纯计算逻辑不应该依赖于其他额外状态,只应该跟输入的数据源有关。需要解耦。
【解决】
store中不再保存dom的ref,在计算逻辑中自己获取dom参与计算。
see: c3abf87

@zyascend
Copy link
Owner Author

zyascend commented Jul 12, 2022

【问题】
缩放导图的逻辑在setup中四处分散,遍布于各个生命周期,不优雅,难维护。
【分析】
应该根据CompositionApi的思想将逻辑集中起来,封装成一个功能单一的hooks。
【解决】

import useAutoZoom from '@/hooks/map/useAutoZoom'
useAutoZoom(renderData) // 封装后 一行代码解决!

see: 484a618

@zyascend
Copy link
Owner Author

【问题】
不同风格的导图的计算逻辑存在公共部分,这些公共部分不应分散于各个计算逻辑中,后期不便维护且代码冗余。
【分析】
抽取公共计算逻辑到一个父类Tree中,所有风格的计算逻辑继承自该父类
【解决】
see: 7fb9a8f

@zyascend
Copy link
Owner Author

zyascend commented Jul 13, 2022

【问题】
导出导图为PNG图片的功能有大问题。
表现为:
1. 【错误处理】一旦导出过程中出错,await的方式没catch error 导致loading无法关闭。
2. 【图片质量】导出的图片大小会受到浏览器窗口大小的影响 而不是导图的原始大小。


浏览器视窗状态

优化前导出效果

优化后导出效果
【分析 & 解决】
  1. 【错误处理】需要封装为Promise即可catch error

  2. 【图片质量】计算导图原始尺寸 --> 重设svg尺寸 --> 将内容移到中间 --> 转换为png

see: 7204653

@zyascend
Copy link
Owner Author

【问题】
扫码登录, 获取二维码的状态的方式是轮询。

【分析】

轮询的缺点

  1. 浪费带宽
  2. 消耗服务器CPU占用
  3. 数据更新不及时

【解决】
see:
aeb70a5
zyascend/mind-map-node@ae7521d

@zyascend
Copy link
Owner Author

zyascend commented Jul 20, 2022

【数据结构的转换与遍历相关优化】

原则: 不递归!!!

1. 扁平结构转树形结构

function flatToTree(data) {
  if (!data) return null
  const values = Object.values(data)
  const treeData = values.filter(item => {
    const { _children, id } = item
    if (_children.length) {
      item._children = values.filter(e => {
        return id === e.parent
      })
    } else {
      item.children = values.filter(e => {
        return id === e.parent
      })
    }
    return item.parent === '-1'
  })
  return treeData[0]
}

2. 获取多叉树的先序遍历序列

/**
 * 获取N叉树的先序序列 = 大纲编辑页的展示顺序
 * @param {*} root
 * @returns
 */
function cyclePreOrder(root) {
  const stack = []
  const res = []
  root.level = 0
  stack.push(root)
  while (stack.length) {
    const cur = stack.pop()
    res.push(cur)
    const len = cur?.children?.length
    if (len) {
      cur.children.forEach((v, i) => {
        // ! 倒序入栈才能顺序出栈
        const child = cur.children[len - 1 - i]
        child.level = cur.level + 1
        stack.push(child)
      })
    }
  }
  return res
}

see: aeffa94

----------update------------
Morris遍历?

@zyascend
Copy link
Owner Author

zyascend commented Jul 22, 2022

当导图节点数达到1000个会发生什么?

查看导出的图片:https://pic.imgdb.cn/item/62dabe6cf54cd3f937ea4b5b.png

1. 数据初始化:接口返回的数据大小达900多k

  • 想办法压缩JSON

比如:需要往节点添加新的子节点 post的data可以定义为如下格式。即post“操作本身”,而不必将整个导图数据传回后台。

2. 数据更新:需要post的请求体超级大,每一个小改动都把整个导图序列化数据传回去???代价巨大。

实测导致问题:
image

  • 需要重写更新数据接口,简化post的请求体
const data = {
  docId: 'd34124dsa2313da',
  type: 'append', // append = 添加子节点  delete = 删除子节点
  currentNode: 'd34dd',
  parentNode: '123ew'
}

3. 节点计算:当1个节点变化,其余所有节点都要重新计算一遍么???

测量一下计算耗时

100个节点
image

500个节点
image

1000个节点
image

结论

耗时部分主要集中在 计算节点宽高
为什么?
节点宽高计算逻辑包括:文字宽高计算 + 图片宽高计算 + 标记宽高计算 + 节点总宽高计算
无疑,文字宽高计算最耗时 因为其内部逻辑有

  • for循环。
  • dom节点的插入删除。

其他计算都是简单计算。

如何优化

耗时部分(文字宽高)优化

1.只进行一次dom计算,算出单个字符所占的宽度,以此为宽度基准
2. 得到宽度基准之后,对于每个节点不必再次插入dom。
3. 宽度基准 * 节点文字长度 = 文本宽度
4. 文本宽度 / 行高 = 文本高度(占几行)

注意点:

  • 选取最宽的字符(W,M, 齉)组成一串来计算宽度基准
  • 宽度基准受字体、字号、style的影响。

节点尺寸方面:

只有节点本身和该节点的所有祖先需要重新计算大小(宽高),而且祖先只需要重新计算外围宽高,其自身内部的文本、图片、标记等元素是不需要重新计算的。

节点位置方面:

横向位置:该节点及其所有子代需要更新。
纵向位置:树的后序序列中,位于该节点后面的所有节点都需更新。

优化方案:

参考Vue3关于编译优化中的patchFlags,对需要更新的节点打标记。根据标记情况决定节点是否需要重新计算。

PATCH_FLAG = 1 // 宽高变化
PATCH_FLAG = 2 // X值变化
PATCH_FLAG = 3 // Y值变化
PATCH_FLAG = 4 // 新增的节点
PATCH_FLAG = 3 // 子节点被删除

@zyascend zyascend changed the title 代码逻辑优化之路 代码优化之路 Jul 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant