-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
perf: Workflow Canvas Rendering #2250
Conversation
Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@@ -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 comment
The 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:
-
Consistent Method Bindings:
- The bindings for methods like
clear_next_node_field
,get_up_node_field_dict
, andget_up_node_field_list
should only appear once in the constructor.
- The bindings for methods like
-
Redundant Imports:
- Remove the duplicate import of
connect
from./teleport
as it's already imported at the beginning.
- Remove the duplicate import of
-
Nullish Coalescing Operator:
- Replace
|| []
with?? []
inget_node_field_list
to handle cases whereconfig?.fields
might be null or undefined more gracefully.
- Replace
-
Cache Management:
- Consider adding caching strategies or memoization to improve performance, especially when the method is called frequently within loops or recursive calculations.
-
Optimize Function Calls:
- Avoid unnecessary function calls that could lead to performance bottlenecks, such as re-retrieving properties from
props.model
.
- Avoid unnecessary function calls that could lead to performance bottlenecks, such as re-retrieving properties from
-
Variable Naming Consistency:
- Ensure variable names follow consistent naming conventions for readability and maintainability.
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.
defineExpose({ validate }) | ||
onMounted(() => { | ||
options.value = getIncomingNode(props.nodeModel.id) | ||
options.value = props.nodeModel.get_up_node_field_list(false, true) | ||
}) | ||
</script> | ||
<style scoped></style> |
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:
declare const t: (...args: any[]) => string; interface NodeModel { graphModel: { getNodeIncomingNode: (id: string) => any[] }; id?: string; // Add more properties as needed }
-
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.
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 comment
The 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 (lf
) and adjusting the view based on certain events. Here's a brief review and suggested improvements:
Review Points:
-
EventListener: The line
lf.value.graphModel.eventCenter.on('anchor:drop', (data: any) => {
correctly adds an event listener for an 'anchor drop' event. It seems intended to handle situations where nodes are dropped onto anchors. -
Node Clearing Logic: The
.clear_next_node_field(false)
method call within the 'anchor:drop' handler is somewhat unclear due to missing context aboutnext_node_field
. However, it appears to clear some fields associated with nodes that might have moved as part of anchoring behavior. -
Timer: The statement
setTimeout(() => { lf.value?.fitView(); }, 0);
ensures thatfitView
is called asynchronously after rendering has finished. This is generally good practice to prevent blocking UI updates during long-running operations.
Optimization Suggestions
-
Consistent Error Handling: Ensure there's consistent error handling when working with asynchronous actions such as clearing node fields or fitting views. For instance, you could wrap these calls in try-catch blocks to catch potential errors gracefully.
-
Event Listener Cleanup: If this logic needs to be removed or modified later, consider implementing a way to cleanly remove the event listener using
off()
, especially if multiple components require similar functionality. This prevents memory leaks over time. -
Type Safety: Verify that the types passed to
on()
and other methods correspond with those expected by the library. Type annotations can help catch type-related issues at compile-time. -
Performance Considerations: While not directly addressed in this snippet, ensure that clearing nodes and fitting views do not involve expensive computations or data manipulations.
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.
perf: Workflow Canvas Rendering