When I use remirror(a rich editor base on prosemirror),
I found code-block has a poor performance.
I found out that it’s because code block use inline decoration , and inline decoration rerendering has a poor performance.
I found that when I edit a node with many inline decorations(remirror code block),
It will re render all span tag after the position where I edit.
I dig into the prosemirror code source, I see that when insert a inline decoration at a node that has many inline decorations, it will update the node’s all inline decorations after the inserting decoration.
reproduce
https://remirror.vercel.app/?path=/story/editors-markdown--basic
My question is:
It is necessary to update so many decorations?
If the answer is YES,why?
If the answer is NO,will prosemirror improve it?
Or what I think is wrong?
marijn
April 19, 2024, 9:22am
2
I cannot reproduce this. If you can show this happening in a minimal setup, rather than a full app, I can take a look. Here’s the code I used.
import {Decoration, DecorationSet, EditorView} from "prosemirror-view"
import {EditorState, Plugin} from "prosemirror-state"
import {schema} from "prosemirror-schema-basic"
let state = EditorState.create({
doc: schema.node("doc", null, schema.node("paragraph", null, schema.text("x ".repeat(40)))),
plugins: [new Plugin({
state: {
init(conf, state) {
let deco = []
for (let i = 1; i < 81; i += 2) {
deco.push(Decoration.inline(i, i + 1, {style: "background: orange"}))
}
return DecorationSet.create(state.doc, deco)
},
apply(tr, deco, old, state) { return deco.map(tr.mapping, state.doc) }
},
props: {
decorations(state) { return this.getState(state) }
}
})]
})
new EditorView(document.body, {state})
I just use the prosemirror website example code
if (alt) {
let attrs = Object.assign({}, state.doc.nodeAt(this.from).attrs, {alt})
dispatch(state.tr.setNodeMarkup(this.from, null, attrs))
}
}
// }
// deco{
import {Decoration, DecorationSet} from "prosemirror-view"
function lintDeco(doc) {
let decos = []
lint(doc).forEach(prob => {
decos.push(Decoration.inline(prob.from, prob.to, {class: "problem"}),
Decoration.widget(prob.from, lintIcon(prob)))
})
return DecorationSet.create(doc, decos)
}
function lintIcon(prob) {
let icon = document.createElement("div")
function lintDeco(doc) {
let decos = []
lint(doc).forEach(prob => {
decos.push(Decoration.inline(prob.from, prob.to, {class: "problem"}))
})
return DecorationSet.create(doc, decos)
}
// plugin{
import {Plugin, TextSelection} from "prosemirror-state"
let lintPlugin = new Plugin({
state: {
init(_, {doc}) { return lintDeco(doc) },
apply(tr, old) { return tr.docChanged ? lintDeco(tr.doc) : old }
},
props: {
decorations(state) { return this.getState(state) },
I mean inserting a decoration. There are no problems when edit but not insert a new decoration.
The gif show:
When I insert a !
after decoration .
, all dom after decoration .
has been update.
marijn
April 22, 2024, 7:15am
5
I see what you mean. The linter just eagerly rendered new icons every time it ran, which obviously requires the library to use the new nodes. This patch shows how to adjust it to be able to preserve the existing icons.
committed 07:13AM - 22 Apr 24 UTC
See https://discuss.prosemirror.net/t/can-improve-inline-decoration-rerendering/… 6368
I see, widget decoration has a key property for reusing dom.
But my situation is there are lots of inline decorations .
Can inline decorations be optimized like that?
marijn
April 22, 2024, 8:21am
7
Inline decorations shouldn’t have this problem, since the library can accurately compare them directly.