-
Notifications
You must be signed in to change notification settings - Fork 2.2k
perf: Workflow Canvas Rendering #2250
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,17 +10,25 @@ import i18n from '@/locales' | |
import { WorkflowType } from '@/enums/workflow' | ||
import { nodeDict } from '@/workflow/common/data' | ||
import { isActive, connect, disconnect } from './teleport' | ||
import { t } from '@/locales' | ||
import { type Dict } from '@/api/type/common' | ||
class AppNode extends HtmlResize.view { | ||
isMounted | ||
r?: any | ||
component: any | ||
app: any | ||
root?: any | ||
VueNode: any | ||
up_node_field_dict?: Dict<Array<any>> | ||
constructor(props: any, VueNode: any) { | ||
super(props) | ||
this.component = VueNode | ||
this.isMounted = false | ||
props.model.clear_next_node_field = this.clear_next_node_field.bind(this) | ||
props.model.get_up_node_field_dict = this.get_up_node_field_dict.bind(this) | ||
props.model.get_node_field_list = this.get_node_field_list.bind(this) | ||
props.model.get_up_node_field_list = this.get_up_node_field_list.bind(this) | ||
|
||
if (props.model.properties.noRender) { | ||
delete props.model.properties.noRender | ||
} else { | ||
|
@@ -30,21 +38,74 @@ class AppNode extends HtmlResize.view { | |
} | ||
} | ||
function getNodesName(num: number) { | ||
let number = num | ||
const number = num | ||
const name = props.model.properties.stepName + number | ||
if (!props.graphModel.nodes?.some((node: any) => node.properties.stepName === name.trim())) { | ||
props.model.properties.stepName = name | ||
} else { | ||
number += 1 | ||
getNodesName(number) | ||
getNodesName(number + 1) | ||
} | ||
} | ||
props.model.properties.config = nodeDict[props.model.type].properties.config | ||
if (props.model.properties.height) { | ||
props.model.height = props.model.properties.height | ||
} | ||
} | ||
get_node_field_list() { | ||
const result = [] | ||
if (this.props.model.type === 'start-node') { | ||
result.push({ | ||
value: 'global', | ||
label: t('views.applicationWorkflow.variable.global'), | ||
type: 'global', | ||
children: this.props.model.properties?.config?.globalFields || [] | ||
}) | ||
} | ||
result.push({ | ||
value: this.props.model.id, | ||
label: this.props.model.properties.stepName, | ||
type: this.props.model.type, | ||
children: this.props.model.properties?.config?.fields || [] | ||
}) | ||
return result | ||
} | ||
get_up_node_field_dict(contain_self: boolean, use_cache: boolean) { | ||
if (!this.up_node_field_dict || !use_cache) { | ||
const up_node_list = this.props.graphModel.getNodeIncomingNode(this.props.model.id) | ||
this.up_node_field_dict = up_node_list | ||
.filter((node) => node.id != 'start-node') | ||
.map((node) => node.get_up_node_field_dict(true, use_cache)) | ||
.reduce((pre, next) => ({ ...pre, ...next }), {}) | ||
} | ||
if (contain_self) { | ||
return { | ||
...this.up_node_field_dict, | ||
[this.props.model.id]: this.get_node_field_list() | ||
} | ||
} | ||
return this.up_node_field_dict ? this.up_node_field_dict : {} | ||
} | ||
|
||
get_up_node_field_list(contain_self: boolean, use_cache: boolean) { | ||
const result = Object.values(this.get_up_node_field_dict(contain_self, use_cache)).reduce( | ||
(pre, next) => [...pre, ...next], | ||
[] | ||
) | ||
const start_node_field_list = this.props.graphModel | ||
.getNodeModelById('start-node') | ||
.get_node_field_list() | ||
return [...start_node_field_list, ...result] | ||
} | ||
|
||
clear_next_node_field(contain_self: boolean) { | ||
const next_node_list = this.props.graphModel.getNodeOutgoingNode(this.props.model.id) | ||
next_node_list.forEach((node) => { | ||
node.clear_next_node_field(true) | ||
}) | ||
if (contain_self) { | ||
this.up_node_field_dict = undefined | ||
} | ||
} | ||
getAnchorShape(anchorData: any) { | ||
const { x, y, type } = anchorData | ||
let isConnect = false | ||
|
@@ -276,7 +337,7 @@ class AppNodeModel extends HtmlResize.model { | |
} | ||
|
||
setAttributes() { | ||
const { t } = i18n.global; | ||
const { t } = i18n.global | ||
this.width = this.get_width() | ||
const isLoop = (node_id: string, target_node_id: string) => { | ||
const up_node_list = this.graphModel.getNodeIncomingNode(node_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several areas where improvements can be made to the provided code:
Here's an optimized version based on these suggestions: @@ -10,9 +10,14 @@
import i18n from '@/locales';
import { WorkflowType } from '@/enums/workflow'
import { nodeDict } from '@/workflow/common/data';
+import { t } from '@/locales';
class AppNode extends HtmlResize.view {
isMounted: boolean;
r?;
component: any;
app: any;
root?: HTMLDivElement;
VueNode: any;
constructor(props: any, VueNode: any) {
super(props);
this.component = VueNode;
this.isMounted = false;
// Single binding statement for all event handlers
props.model.events = {
clearNextNodeField: this.clear_next_node_field.bind(this),
getUpNodeFieldDict: this.get_up_node_field_dict.bind(this),
getNodeFieldList: this.getNodeFieldList.bind(this)
};
if (props.model.properties.noRender) {
delete props.model.properties.noRender;
}
function getNodesName(num: number): string {
const name = `${props.model.properties.stepName}-${num}`;
if (!props.graphModel.nodes.some((node: any) => node.properties.stepName === name)) {
props.model.properties.stepName = name;
} else {
return getNodesName(++num);
}
}
props.model.properties.config = nodeDict[props.model.type].properties.config;
if (props.model.properties.height) {
props.model.height = props.model.properties.height;
}
}
getNodeFieldList(): Array<{ value: string; label: string; type: string; children?: Array<any> }> {
const result: Array<{ value: string; label: string; type: string; children?: Array<any> }> = [];
if (this.props.model.type === 'start-node') {
result.push({
value: 'global',
label: t('views.applicationWorkflow.variable.global'),
type: 'global',
children: this.props.model.properties?.config?.globalFields ?? []
});
}
result.push({
value: this.props.model.id,
label: this.props.model.properties.stepName,
type: this.props.model.type,
children: this.props.model.properties?.config?.fields ?? []
});
return result;
}
getUpNodeFieldDict(containSelf: boolean, useCache: boolean): Record<string, Array<{ value: string; label: string; type: string; children?: Array<any> }>> | undefined {
let upNodeList = this.props.graphModel.getNodeIncomingNode(this.props.model.id);
if (!upNodeList.length || upNodeList.every(n => n.id !== 'start-node')) {
return undefined;
}
let upNodeFieldDict: Record<string, Array<{ value: string; label: string; type: string; children?: Array<any> }>> = {};
for (const node of upNodeList.filter(node => node.id !== 'start-node')) {
const fields = node.getUpNodeFieldDict(true, useCache)?.[node.id];
upNodeFieldDict[node.id] = fields;
}
if (containSelf) {
upNodeFieldDict[this.props.model.id] = this.getNodeFieldList();
}
return upNodeFieldDict;
}
getUpNodeFieldList(containSelf: boolean, useCache: boolean): Array<{ value: string; label: string; type: string; children?: Array<any> }> {
const baseResult = Object.values(this.getUpNodeFieldDict(containSelf, useCache));
if (baseResult.length > 0) {
const startNodeFieldList = this.props.graphModel.getNodeModelById('start-node').get_nodeFieldList();
return [...startNodeFieldList, ...baseResult.flat()];
}
return [];
}
clearNextNodeField(containSelf: boolean) {
const nextNodeList = this.props.graphModel.getNodeOutgoingNode(this.props.model.id).filter(node => node.type !== WorkflowType.StartNode);
nextNodeList.forEach(node => {
node.clearNextNodeField(true);
});
if (containSelf) {
this.upNodeFieldDict = undefined;
}
}
} These changes ensure better organization and consistency across the codebase while improving readability and potentially enhancing performance through less redundant operations. |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,6 +103,10 @@ const renderGraphData = (data?: any) => { | |
lf.value.deleteEdge(id) | ||
}) | ||
}) | ||
lf.value.graphModel.eventCenter.on('anchor:drop', (data: any) => { | ||
// 清除当前节点下面的子节点的所有缓存 | ||
data.nodeModel.clear_next_node_field(false) | ||
}) | ||
|
||
setTimeout(() => { | ||
lf.value?.fitView() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided code appears to be setting up event listeners for changes in a graph layout library ( Review Points:
Optimization Suggestions
Overall, the code looks functional for managing interactions and updating the graphical representation based on user input. Minor adjustments like adding clear documentation around unclear parts of the logic and ensuring robust error handling would enhance its maintenance and future reliability. |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code appears to be handling node field changes and validation within an application workflow. It includes fetching incoming nodes, validating the current selection of fields, and handling events related to refreshing node fields.
Here are some optimizations and improvements suggested:
Avoiding Deep Copies: The
_getIncomingNode
function modifies thevalue
array in place rather than cloning it before modifications. This approach reduces overhead but may affect performance if used improperly. Ensure that modifying arrays does not have side effects elsewhere in your application.TypeScript Annotations: Adding TypeScript annotations for type safety can help catch errors early during development. For example:
Refactoring Event Listeners: Consider refactoring event listeners outside of the main logic to improve maintainability and reduce complexity. You could create separate functions or classes for managing these event subscriptions.
Comments and Documentation: Add comments explaining complex logic or assumptions for clarity. Also consider adding documentation comments or a README.md file for better understanding of the code's purpose and functionality.
Testing: Implement unit tests for specific parts of the codebase, especially those involving state management and data fetching.
Error Handling: Enhance error handling by adding more detailed messages returned from promises or logging exceptions for easier debugging.
By addressing these points, you can make the code cleaner, more efficient, and potentially easier to maintain and extend in the future.