Skip to content

Conversation

@UmbraCi
Copy link
Contributor

@UmbraCi UmbraCi commented Aug 28, 2025

This PR introduces a new public API add_nodes to efficiently add multiple nodes in one batch.\n\nKey changes:\n- Add add_nodes method in runtime to build nodes without repeated relayout/redraw\n- Update legacy bundle to expose the new API\n- Provide example/test_batch_add.html to benchmark and demonstrate batch addition\n\nWhy:\nAdding nodes one-by-one causes repeated layout and paint. This optimization significantly reduces the cost for bulk insert scenarios.\n\nNotes:\n- API surface follows existing naming and event conventions (fires edit:add_nodes)\n- Backward-compatible; no breaking change\n\nPlease let me know if additional docs or tests are required.

…add_nodes method to add multiple nodes efficiently in a single layout pass\n- Update legacy bundle accordingly\n- Add example/test_batch_add.html to demonstrate and benchmark batch addition\n\nRefs: performance optimization for bulk node insertion
Copy link
Owner

@hizzgdev hizzgdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks to @UmbraCi , leave few comments, PTAL!

src/jsmind.js Outdated
}

// Phase 2: Unified layout calculation and redraw (execute only once)
if (created_nodes.some(node => node !== null)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just test !!created_nodes, because null-value won't be added in the list, it's checked at line 495

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在使用库的custom_node_render方法自定义渲染节点的时候,若节点数据的topic为空值时,会直接走进custom_node_render方法渲染自定义的DOM,实际上有些节点在使用场景中,我需要渲染一些span标签用以展示状态,类似于一些UI库的label,为了解决topic为空不走进custom_node_render的问题,我不得不把传入jm.show的数据的空topic转换成 “ ”(中间有个空格),你觉得这个逻辑有必要修复吗

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在使用库的custom_node_render方法自定义渲染节点的时候,若节点数据的topic为空值时,会直接走进custom_node_render方法渲染自定义的DOM,实际上有些节点在使用场景中,我需要渲染一些span标签用以展示状态,类似于一些UI库的label,为了解决topic为空不走进custom_node_render的问题,我不得不把传入jm.show的数据的空topic转换成 “ ”(中间有个空格),你觉得这个逻辑有必要修复吗

你最后提到的“这个逻辑” 是 “topic为空不走进custom_node_render” 吗?如果是的话,我不建议进行修改。如果 node.topic 不存在,说明这个 node 没有实际的语义,你需要展示状态的话,应该给这个 node 设定具体的语义,既然使用了 custom_node_render, 那把一个带有语义的文本写到 node.topic 里我感觉才比较合理。 @UmbraCi 这是我的想法。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在使用库的custom_node_render方法自定义渲染节点的时候,若节点数据的topic为空值时,会直接走进custom_node_render方法渲染自定义的DOM,实际上有些节点在使用场景中,我需要渲染一些span标签用以展示状态,类似于一些UI库的label,为了解决topic为空不走进custom_node_render的问题,我不得不把传入jm.show的数据的空topic转换成 “ ”(中间有个空格),你觉得这个逻辑有必要修复吗

你最后提到的“这个逻辑” 是 “topic为空不走进custom_node_render” 吗?如果是的话,我不建议进行修改。如果 node.topic 不存在,说明这个 node 没有实际的语义,你需要展示状态的话,应该给这个 node 设定具体的语义,既然使用了 custom_node_render, 那把一个带有语义的文本写到 node.topic 里我感觉才比较合理。 @UmbraCi 这是我的想法。

基础版本的用法是让脑图节点显示文本,进阶玩法应该是让节点能够渲染类似于“富文本”之类的视图,比如一个超链接(可以点击,且显示自定义)、一个状态标签(label组件),这种节点其实是不想渲染出文本的,但是因为custom_node_render方法进入前校验了topic字段是否为空,会导致不渲染自定义节点的问题,为了拓展性,就算不处理,是否加个配置项要好一些呢?我的想法是,既然有custom_node_render这个方法,那是否渲染这个逻辑应该也可以交给它来判断,这个方法也是可以返回boolean值的

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

就算不处理,是否加个配置项要好一些呢
既然有custom_node_render这个方法,那是否渲染这个逻辑应该也可以交给它来判断,这个方法也是可以返回boolean值的

技术上可行。我这里考虑的主要是脑图本身数据对象的完整性。如果 node.topic 为空也能代表一定的意义的话,那这部分意义也应该体现在数据对象中。如果这部分意义无法体现在 node.topic 中,我们也可以设计出一个新的字段来代替 topic 去支持 richContent。 但无论如何,我们需要在数据对象中保留它的语义,而不能仅靠 custom_node_render 给节点赋予语义。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

就算不处理,是否加个配置项要好一些呢
既然有custom_node_render这个方法,那是否渲染这个逻辑应该也可以交给它来判断,这个方法也是可以返回boolean值的

技术上可行。我这里考虑的主要是脑图本身数据对象的完整性。如果 node.topic 为空也能代表一定的意义的话,那这部分意义也应该体现在数据对象中。如果这部分意义无法体现在 node.topic 中,我们也可以设计出一个新的字段来代替 topic 去支持 richContent。 但无论如何,我们需要在数据对象中保留它的语义,而不能仅靠 custom_node_render 给节点赋予语义。

我的想法是:
给node对象增加一个属性,用以判断是否需要在topic为空的时候也走到richContent中,目前我的做法是遍历整个脑图数据结构,给有需要展示richContent的节点根据data里面的富文本要展示出来的字段来判断
if(node.topic === '' && node.data.label){
// 设置node.topic为一个空格符
}
其实跟你说的这个增加属性的办法殊途同归,只不过我是用空格符强行去满足Boolean(topic),题外话---其实在一些eslint规范中是不推荐直接使用if(Boolean( 字符串 ))判断的;
你看下是否有必要增加这样一个字段来在topic为空的时候让节点继续走到custom_node_render执行渲染呢

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样的话,我们可以把这段逻辑修改成 node.topic has no value && node.data is empty , 你感觉如何?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样的话,我们可以把这段逻辑修改成 node.topic has no value && node.data is empty , 你感觉如何?

是可以达到效果,直接用data对象是否为空来判断,有没有可能会影响到现有其他用户的正常业务逻辑,这一点我有担忧,如果有影响,用户更新库以后,也要修改原本“正常”的业务代码

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,这样破坏兼容性了。那我们就加一个 content 字段吧,把它当做 topic 的升级版本。

node.content = {
    type: 'plain',
    value: 'topic'
}

node.content = {
    type: 'html',
    value: '<span>...</span>'
}

node.content = {
    type: 'some-other-type',
    value: any object
}

jsmind 支持前两种,其它 type 需要用户自行渲染。

然后判断逻辑改为

if (topic has value or content has value ) {
    custom_node_render();
}

- Refactor add_node to use private helper methods _add_node_data and _refresh_node_ui
- Add add_nodes method for optimized batch node creation
- Improve performance by reducing layout calculations from O(n) to O(1)
- Add unit tests for add_nodes functionality
- Remove add_nodes from legacy version as suggested in PR feedback

Addresses feedback from PR hizzgdev#667:
- Split add_node into smaller, reusable methods
- Simplify condition checking in add_nodes
- Focus only on modern ES6 version in src/ directory
- Add comprehensive unit tests
- Add 10 test cases covering various scenarios:
  - Error handling (not editable, parent not found, invalid input)
  - Core functionality (method calls, parameter passing)
  - Event handling and return values
  - Failed node creation scenarios
- Use mocking to avoid DOM-related issues in test environment
- Ensure proper testing of the batch node creation workflow
- All tests pass successfully
- Add performance test for large-scale node creation (1000 nodes)
- Add complex data structure test with Unicode and multilingual support
- Add comprehensive direction handling test (13 different direction types)
- Refactor create_fake_mind to accept options parameter for better flexibility
- Remove default editable:true from create_fake_mind, require explicit declaration
- Update all test cases to explicitly pass editable:true when needed
- Ensure 100% test pass rate with improved test design and clarity

Performance improvements verified:
- Batch operations call _refresh_node_ui only once (O(1) vs O(n))
- Large node creation completes within 100ms performance threshold
- Complex data structures and Unicode characters handled correctly

Total test coverage: 13 test cases covering error handling, core functionality,
performance optimization, data integrity, and edge cases.
@UmbraCi
Copy link
Contributor Author

UmbraCi commented Aug 29, 2025

Many thanks to @UmbraCi , leave few comments, PTAL!

代码已修改,请重新CR @hizzgdev

expect(result).toHaveLength(1000);
expect(jsmind._add_node_data).toHaveBeenCalledTimes(1000);
expect(jsmind._refresh_node_ui).toHaveBeenCalledTimes(1); // Key performance benefit
expect(end - start).toBeLessThan(100); // Should complete within 100ms
Copy link
Owner

@hizzgdev hizzgdev Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!
TBH, I don't think we have to check the performance here, because the methods _add_node_data and _refresh_node_ui are mocked. but let's keep it anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!  凉! TBH, I don't think we have to check the performance here, because the methods _add_node_data and _refresh_node_ui are mocked. but let's keep it anyway.TBH,我认为我们不必在这里检查性能,因为 _add_node_data_refresh_node_ui 的方法都被嘲笑了。但无论如何,我们还是保留它吧。

不必检查性能的原因是什么呢

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

因为这里测不出实际的性能, 真正的 _add_node_data_refresh_node_ui 方法在测试中被 jest.fn 给替代了。

Copy link
Owner

@hizzgdev hizzgdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@hizzgdev hizzgdev merged commit 5757bab into hizzgdev:master Aug 29, 2025
4 checks passed
@UmbraCi
Copy link
Contributor Author

UmbraCi commented Sep 4, 2025

@hizzgdev 是否需要先发版一次呢,npm仓库没更新

@hizzgdev
Copy link
Owner

hizzgdev commented Sep 4, 2025

感谢提醒 @UmbraCi ,0.9.0 已经发布了 https://www.npmjs.com/package/jsmind

@UmbraCi
Copy link
Contributor Author

UmbraCi commented Sep 11, 2025

@hizzgdev 增加节点的方法支持递归添加子节点PR
#671

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants