Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shy-moose-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@wangeditor-next/core': patch
---

优化编辑器内容过多表格数据容易卡顿
31 changes: 27 additions & 4 deletions packages/core/src/editor/plugins/with-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import $, {
} from '../../utils/dom'
import { Key } from '../../utils/key'
import { findCurrentLineRange } from '../../utils/line'
import { EDITOR_TO_SELECTION, NODE_TO_KEY } from '../../utils/weak-maps'
import { EDITOR_TO_SELECTION, NODE_TO_KEY, NODE_TO_VNODE, NODE_TO_HTML } from '../../utils/weak-maps'
import { DomEditor } from '../dom-editor'
import { ElementWithId } from '../interface'

Expand Down Expand Up @@ -102,17 +102,31 @@ export const withContent = <T extends Editor>(editor: T) => {
matches.push(...getMatches(e, commonPath))
break
}
case 'set_selection': {
if ((op as any).newProperties?.focus?.path) {
matches.push(...getMatches(e, (op as any).newProperties?.focus?.path))
matches.push(...getMatches(e, (op as any).properties?.focus?.path))
}
break
}
default:
}

// 执行原本的 apply
apply(op)

// 更新 node 和 key 的映射
for (const [path, key] of matches) {
const [node] = Editor.node(e, path)

NODE_TO_KEY.set(node, key)
// 删除node对应的 vnode 和 html
if ((node as any)?.type) {
if (NODE_TO_VNODE.has(node)) {
NODE_TO_VNODE.delete(node)
}
if (NODE_TO_HTML.has(node)) {
NODE_TO_HTML.delete(node)
}
}
}
}

Expand Down Expand Up @@ -168,8 +182,17 @@ export const withContent = <T extends Editor>(editor: T) => {
// 获取 html (去掉了格式化 2021.12.10)
e.getHtml = (): string => {
const { children = [] } = e
const html = children.map(child => node2html(child, e)).join('')

const html = children.map(child => {
// 先从缓存中获取
const cached = NODE_TO_HTML.get(child)

if (cached) { return cached }
const htmlStr = node2html(child, e)

NODE_TO_HTML.set(child, htmlStr)
return htmlStr
}).join('')
return html
}

Expand Down
20 changes: 18 additions & 2 deletions packages/core/src/text-area/update-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import {
NODE_TO_ELEMENT,
ELEMENT_TO_NODE,
EDITOR_TO_WINDOW,
NODE_TO_VNODE,
NODE_TO_INDEX
} from '../utils/weak-maps'


function genElemId(id: number) {
return `w-e-textarea-${id}`
}
Expand Down Expand Up @@ -58,7 +61,6 @@ function genRootElem(elemId: string, readOnly = false): Dom7Array {

return $elem
}

/**
* 获取 editor.children 渲染 DOM
* @param textarea textarea
Expand All @@ -72,9 +74,23 @@ function updateView(textarea: TextArea, editor: IDomEditor) {
// 生成 newVnode
const newVnode = genRootVnode(elemId, readOnly)
const content = editor.children || []

newVnode.children = content.map((node, i) => {
let vnode = node2Vnode(node, i, editor, editor)
if (NODE_TO_VNODE.has(node)) {
const [index, cached] = NODE_TO_VNODE.get(node as any) as any
if (cached) {
if (index !== i) {
// 设置相关 weakMap 信息
NODE_TO_INDEX.set(node, i)
NODE_TO_VNODE.set(node, [i, cached])
}
return cached
}
}
const vnode = node2Vnode(node, i, editor, editor)

normalizeVnodeData(vnode) // 整理 vnode.data 以符合 snabbdom 的要求
NODE_TO_VNODE.set(node as any, [i, vnode])
return vnode
})

Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/utils/weak-maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export const EDITOR_TO_CONFIG = new WeakMap<IDomEditor, IEditorConfig>()
export const IS_FIRST_PATCH = new WeakMap<TextArea, boolean>()
export const TEXTAREA_TO_PATCH_FN = new WeakMap<TextArea, PatchFn>()
export const TEXTAREA_TO_VNODE = new WeakMap<TextArea, VNode>()

export const NODE_TO_VNODE = new WeakMap<Node, [number, VNode]>()
export const NODE_TO_HTML = new WeakMap<Node, String>()
/**
* Two weak maps that allow us rebuild a path given a node. They are populated
* at render time such that after a render occurs we can always backtrack.
Expand Down