-
Notifications
You must be signed in to change notification settings - Fork 24
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: reduce re-rendering #353
The head ref may contain hidden characters: "309-\u7F16\u8F91\u5668\u5185\u5BB9\u8FC7\u591A\u8F93\u5165\u6570\u636E\u5BB9\u6613\u5361\u987F"
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughThe changes in this pull request focus on enhancing the functionality of the textarea view rendering in the Changes
Possibly related PRs
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
packages/core/src/text-area/update-view.ts (1)
Line range hint
47-54
: Remove unused parameter_readOnly
fromgenRootElem
.The
_readOnly
parameter in thegenRootElem
function is not used within the function body. Removing it can improve code clarity and avoid confusion.Apply this diff to remove the unused parameter:
-function genRootElem(elemId: string, _readOnly = false): Dom7Array { +function genRootElem(elemId: string): Dom7Array {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
packages/core/src/text-area/update-view.ts
(8 hunks)
🔇 Additional comments (1)
packages/core/src/text-area/update-view.ts (1)
66-108
: Efficient implementation of diffBySelection
.
The diffBySelection
function effectively optimizes rendering by updating only the necessary parts of the virtual DOM based on the current selection, reducing unnecessary re-renders. The logic appears sound and should enhance performance.
1d11ac0
to
5262406
Compare
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.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (4)
packages/core/src/text-area/update-view.ts (4)
48-48
: Consider removing the unused parameter.The
_readOnly
parameter is not used in the function implementation. Consider removing it to improve code clarity.-function genRootElem(elemId: string, _readOnly = false): Dom7Array { +function genRootElem(elemId: string): Dom7Array {
104-120
: Consider improving null checks and extracting selection logic.The selection-based diffing optimization is good, but the code could be more maintainable.
Consider these improvements:
- Use optional chaining for null checks
- Extract selection caching logic to a separate function
- if ( - prevVnode - && cacheSelection - && Range.isCollapsed(cacheSelection) - ) { + if (prevVnode?.children && cacheSelection && Range.isCollapsed(cacheSelection)) { newVnode.children = diffBySelection(prevVnode, content, editor) } else { newVnode.children = content.map((node, i) => {+ function shouldUseSelectionDiff( + prevVnode: VNode | undefined, + cacheSelection: Range | undefined + ): boolean { + return Boolean( + prevVnode?.children && + cacheSelection && + Range.isCollapsed(cacheSelection) + ) + } + // In updateView function - if ( - prevVnode - && cacheSelection - && Range.isCollapsed(cacheSelection) - ) { + if (shouldUseSelectionDiff(prevVnode, cacheSelection)) {
147-147
: Improve error handling consistency.The null checks could be more consistent and use optional chaining.
- if (curVnode == null || patchFn == null) { return } + if (!curVnode || !patchFn) return // ... later in the code ... - if (textareaElem == null) { return } + if (!textareaElem) returnAlso applies to: 157-157
179-180
: Simplify window assignment.The window assignment could be simplified using optional chaining.
- const window = getDefaultView(textareaElem) - - // eslint-disable-next-line no-unused-expressions - window && EDITOR_TO_WINDOW.set(editor, window) + getDefaultView(textareaElem)?.pipe(window => + EDITOR_TO_WINDOW.set(editor, window) + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
packages/core/src/text-area/update-view.ts
(8 hunks)packages/core/src/utils/weak-maps.ts
(2 hunks)
🔇 Additional comments (4)
packages/core/src/utils/weak-maps.ts (2)
7-9
: LGTM! Import organization looks good.
The consolidation and reordering of imports improve code organization without affecting functionality.
Also applies to: 12-12, 15-15, 18-20
80-80
: Verify the re-rendering optimization impact.
The new TEXTAREA_TO_SELECTION
WeakMap is a good addition for tracking textarea selections efficiently. This aligns well with the PR's objective of reducing re-rendering by allowing precise tracking of selection changes.
Let's verify the optimization's implementation:
✅ Verification successful
Let me gather more context about the selection handling and re-rendering logic.
WeakMap optimization effectively reduces re-rendering scope
The TEXTAREA_TO_SELECTION
WeakMap is correctly implemented as part of an optimization strategy that reduces unnecessary re-renders:
- It caches the previous selection state and uses
Range.isCollapsed
check to determine if only a small portion needs updating - When the selection is collapsed (cursor position), it triggers
diffBySelection
which performs targeted updates instead of re-rendering all content - The WeakMap properly maintains the selection state while allowing garbage collection of unused TextArea references
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check how the new WeakMap is utilized for optimization
# Look for usage of TEXTAREA_TO_SELECTION in the codebase
rg "TEXTAREA_TO_SELECTION" -A 5
# Check for related selection handling and re-rendering logic
ast-grep --pattern 'function diffBySelection($$$) {
$$$
}'
Length of output: 1860
Script:
#!/bin/bash
# Check the update-view.ts file content to understand the selection handling logic
rg -B 5 -A 10 "cacheSelection" packages/core/src/text-area/update-view.ts
# Look for any selection-related performance optimizations
rg -B 3 -A 3 "Range.isCollapsed|selection" packages/core/src/text-area/update-view.ts
Length of output: 1507
packages/core/src/text-area/update-view.ts (2)
37-37
: LGTM! Simplified contentEditable assignment.
The change to !readOnly
improves code clarity while maintaining the same functionality.
65-89
: 🛠️ Refactor suggestion
Consider handling multi-line selections and array bounds.
The current implementation has two potential issues:
- It only updates the node at
startIndex
, which might miss updates in multi-line selections wherefocus.path[0]
differs fromanchor.path[0]
. - There's no validation that
startIndex
is within the bounds of the content array.
Let's verify if there are any existing tests covering these edge cases:
Consider updating the implementation to handle these cases:
function diffBySelection(
prevVnode: VNode,
content: Descendant[],
editor: IDomEditor,
): VNode[] {
const selection = editor.selection
if (!selection) {
return prevVnode.children as VNode[]
}
const { anchor, focus } = selection
- // 确定更新范围
- const startIndex = Math.min(anchor.path[0], focus.path[0])
+ // Validate and get update range
+ const startIndex = Math.min(anchor.path[0], focus.path[0])
+ const endIndex = Math.max(anchor.path[0], focus.path[0])
+
+ if (startIndex < 0 || startIndex >= content.length) {
+ return prevVnode.children as VNode[]
+ }
// 克隆数组并更新指定位置
const newChildren = [...(prevVnode.children || [])] as VNode[]
- const newNode = node2Vnode(content[startIndex], startIndex, editor, editor)
-
- normalizeVnodeData(newNode)
- newChildren[startIndex] = newNode
+
+ // Update all nodes in selection range
+ for (let i = startIndex; i <= endIndex && i < content.length; i++) {
+ const newNode = node2Vnode(content[i], i, editor, editor)
+ normalizeVnodeData(newNode)
+ newChildren[i] = newNode
+ }
return newChildren
}
Changes Overview
Implementation Approach
Testing Done
Verification Steps
Additional Notes
Checklist
Related Issues
Summary by CodeRabbit
New Features
Bug Fixes
Refactor