fix: 懒加载会导致协同错误
This commit is contained in:
parent
db7105a28e
commit
919e7cac8c
|
@ -32,6 +32,7 @@ import { Backspace, Enter, Left, Right, Up, Down, Default } from './typing';
|
|||
import { $ } from '../node';
|
||||
import { isNode, isNodeEntry } from '../node/utils';
|
||||
import { CardActiveTrigger, CardType } from './enum';
|
||||
import { updateIndex } from '../ot/utils';
|
||||
import './index.css';
|
||||
|
||||
class CardModel implements CardModelInterface {
|
||||
|
@ -104,34 +105,37 @@ class CardModel implements CardModelInterface {
|
|||
this.classes[card.cardName] = card;
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.renderAsnycComponents);
|
||||
this.editor.scrollNode?.on('scroll', this.renderAsnycComponents);
|
||||
window.addEventListener('scroll', this.renderAsnycComponents);
|
||||
window.addEventListener('resize', this.renderAsyncComponents);
|
||||
this.editor.scrollNode
|
||||
?.get<HTMLElement>()
|
||||
?.addEventListener('scroll', this.renderAsyncComponents);
|
||||
window.addEventListener('scroll', this.renderAsyncComponents);
|
||||
}
|
||||
|
||||
renderAsnycComponents = () => {
|
||||
renderAsyncComponents = async () => {
|
||||
if (this.renderTimeout) clearTimeout(this.renderTimeout);
|
||||
this.renderTimeout = setTimeout(() => {
|
||||
const components = this.asyncComponents.concat();
|
||||
components.forEach((card) => {
|
||||
components.forEach(async (card) => {
|
||||
// 在视图内才渲染卡片
|
||||
if (
|
||||
card.root.length === 0 ||
|
||||
this.editor.root.inViewport(card.root, true)
|
||||
) {
|
||||
if (card.root.length > 0 && card.loading) {
|
||||
card.getCenter().empty();
|
||||
this.renderComponent(card);
|
||||
}
|
||||
this.asyncComponents.splice(
|
||||
this.asyncComponents.findIndex(
|
||||
(component) => component === card,
|
||||
),
|
||||
1,
|
||||
);
|
||||
if (card.root.length > 0 && card.loading) {
|
||||
if (card.destroy) card.destroy();
|
||||
card.getCenter().empty();
|
||||
this.renderComponent(card);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}, 50);
|
||||
};
|
||||
|
||||
add(clazz: CardEntry) {
|
||||
|
@ -483,7 +487,7 @@ class CardModel implements CardModelInterface {
|
|||
const parent = card.root.parent();
|
||||
this.removeNode(card);
|
||||
list.addBr(range.startNode);
|
||||
if (parent && node.isEmpty(parent)) {
|
||||
if (parent && node.isEmpty(parent) && !this.editor.ot.isStopped) {
|
||||
if (parent.isEditable()) {
|
||||
node.html(parent, '<p><br /></p>');
|
||||
range.select(parent, true);
|
||||
|
@ -511,7 +515,7 @@ class CardModel implements CardModelInterface {
|
|||
|
||||
const parent = card.root.parent();
|
||||
this.removeNode(card);
|
||||
if (parent && node.isEmpty(parent)) {
|
||||
if (parent && node.isEmpty(parent) && !this.editor.ot.isStopped) {
|
||||
if (parent.isEditable()) {
|
||||
node.html(parent, '<p><br /></p>');
|
||||
} else {
|
||||
|
@ -645,6 +649,12 @@ class CardModel implements CardModelInterface {
|
|||
const asyncRenderCards: Array<CardInterface> = [];
|
||||
cards.each((node) => {
|
||||
const cardNode = $(node);
|
||||
cardNode.find(`${CARD_SELECTOR},${READY_CARD_SELECTOR}`).remove();
|
||||
cardNode.empty();
|
||||
});
|
||||
cards.each((node) => {
|
||||
const cardNode = $(node);
|
||||
if (cardNode.length === 0 || !cardNode[0].parentNode) return;
|
||||
const readyKey = cardNode.attributes(READY_CARD_KEY);
|
||||
const key = cardNode.attributes(CARD_KEY);
|
||||
const name = readyKey || key;
|
||||
|
@ -680,7 +690,10 @@ class CardModel implements CardModelInterface {
|
|||
);
|
||||
}
|
||||
});
|
||||
if (readyKey) cardNode.replaceWith(card.root);
|
||||
if (readyKey) {
|
||||
cardNode.replaceWith(card.root);
|
||||
cardNode.remove();
|
||||
}
|
||||
this.components.push(card);
|
||||
|
||||
// 重新渲染
|
||||
|
@ -691,7 +704,7 @@ class CardModel implements CardModelInterface {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
let isTriggerRenderAsync = false;
|
||||
asyncRenderCards.forEach((card) => {
|
||||
if (lazyRender && (card.constructor as CardEntry).lazyRender) {
|
||||
if (card.beforeRender) {
|
||||
|
@ -703,16 +716,16 @@ class CardModel implements CardModelInterface {
|
|||
);
|
||||
}
|
||||
}
|
||||
if (!this.asyncComponents.includes(card))
|
||||
this.asyncComponents.push(card);
|
||||
isTriggerRenderAsync = true;
|
||||
this.asyncComponents.push(card);
|
||||
} else {
|
||||
this.renderComponent(card);
|
||||
}
|
||||
});
|
||||
if (callback) callback(asyncRenderCards.length);
|
||||
if (this.asyncComponents.length > 0) {
|
||||
if (isTriggerRenderAsync) {
|
||||
// 触发当前在视图内的卡片渲染
|
||||
this.renderAsnycComponents();
|
||||
this.renderAsyncComponents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,6 +750,7 @@ class CardModel implements CardModelInterface {
|
|||
this.render(center);
|
||||
}
|
||||
card.didRender();
|
||||
updateIndex(card.root);
|
||||
}
|
||||
|
||||
removeComponent(card: CardInterface): void {
|
||||
|
@ -765,9 +779,11 @@ class CardModel implements CardModelInterface {
|
|||
|
||||
destroy() {
|
||||
this.gc();
|
||||
window.removeEventListener('resize', this.renderAsnycComponents);
|
||||
this.editor.scrollNode?.off('scroll', this.renderAsnycComponents);
|
||||
window.removeEventListener('scroll', this.renderAsnycComponents);
|
||||
window.removeEventListener('resize', this.renderAsyncComponents);
|
||||
this.editor.scrollNode
|
||||
?.get<HTMLElement>()
|
||||
?.removeEventListener('scroll', this.renderAsyncComponents);
|
||||
window.removeEventListener('scroll', this.renderAsyncComponents);
|
||||
}
|
||||
|
||||
// 焦点移动到上一个 Block
|
||||
|
|
|
@ -307,7 +307,11 @@ class ChangeRange implements ChangeRangeInterface {
|
|||
}
|
||||
}
|
||||
|
||||
if (startNode.isEditable() && startNode.children().length === 0) {
|
||||
if (
|
||||
startNode.isEditable() &&
|
||||
startNode.children().length === 0 &&
|
||||
!this.engine.ot.isStopped
|
||||
) {
|
||||
startNode.html('<p><br /></p>');
|
||||
}
|
||||
//在非折叠,或者当前range对象和selection中的对象不一致的时候重新设置range
|
||||
|
|
|
@ -49,7 +49,10 @@ class Consumer implements ConsumerInterface {
|
|||
) {
|
||||
const { card } = this.engine;
|
||||
const cardComponent = card.find(node);
|
||||
if (cardComponent) card.renderComponent(cardComponent);
|
||||
if (cardComponent) {
|
||||
cardComponent.getCenter().empty();
|
||||
card.renderComponent(cardComponent);
|
||||
}
|
||||
}
|
||||
const childNode = Array.from(node.childNodes).filter((node) => {
|
||||
const childNode = $(node);
|
||||
|
|
|
@ -207,6 +207,10 @@ class OTModel extends EventEmitter2 implements OTInterface {
|
|||
if (this.mutation) this.mutation.stop();
|
||||
}
|
||||
|
||||
isStopped() {
|
||||
return this.mutation?.isStopped ?? false;
|
||||
}
|
||||
|
||||
startMutationCache() {
|
||||
if (this.mutation) this.mutation.startCache();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class Mutation extends EventEmitter2 implements MutationInterface {
|
|||
private node: NodeInterface;
|
||||
private engine: EngineInterface;
|
||||
private doc?: DocInterface | Doc;
|
||||
private isStopped: boolean;
|
||||
isStopped: boolean;
|
||||
private observer: MutationObserver;
|
||||
private producer: Producer;
|
||||
private isCache: boolean = false;
|
||||
|
|
|
@ -554,33 +554,34 @@ class Producer extends EventEmitter2 {
|
|||
isTransient = tMapValue;
|
||||
}
|
||||
// 标记节点为已处理
|
||||
const { addedNodes } = record;
|
||||
addedNodes.forEach((addNode) => {
|
||||
record.addedNodes.forEach((addNode) => {
|
||||
addNode['__card_rendered'] = true;
|
||||
});
|
||||
// 需要比对异步加载的卡片子节点(body -> center -> 非 ui 和 data-transient-element节点)是否已经处理完,处理完就移除掉卡片根节点的 CARD_LOADING_KEY 标记
|
||||
// card.root.removeAttributes(CARD_LOADING_KEY);
|
||||
// 判断卡片下面的节点
|
||||
const isRendered = cardElement.isEditableCard()
|
||||
? cardElement
|
||||
.find(CARD_CENTER_SELECTOR)
|
||||
.children()
|
||||
.toArray()
|
||||
.every((child) => {
|
||||
if (child.length === 0) return true;
|
||||
const attributes = child.attributes();
|
||||
if (
|
||||
attributes[DATA_ELEMENT] === UI ||
|
||||
!!attributes[DATA_TRANSIENT_ELEMENT]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (child[0]['__card_rendered'] === true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
: true;
|
||||
const children = cardElement
|
||||
.find(CARD_CENTER_SELECTOR)
|
||||
.children()
|
||||
.toArray();
|
||||
|
||||
const isRendered =
|
||||
children.length > 0 &&
|
||||
children.every((child) => {
|
||||
if (child.length === 0) return true;
|
||||
const attributes = child.attributes();
|
||||
if (
|
||||
(attributes[DATA_ELEMENT] === UI ||
|
||||
!!attributes[DATA_TRANSIENT_ELEMENT]) &&
|
||||
!child.hasClass(CARD_LOADING_KEY)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (child[0]['__card_rendered'] === true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (isRendered) {
|
||||
const handleEditableCard = (
|
||||
editableCard: NodeInterface,
|
||||
|
@ -591,15 +592,9 @@ class Producer extends EventEmitter2 {
|
|||
.every((childCard) => {
|
||||
const childLoading =
|
||||
childCard.attributes(CARD_LOADING_KEY);
|
||||
if (!childLoading) {
|
||||
return true;
|
||||
}
|
||||
// 如果子卡片是懒加载的,则算已加载完成
|
||||
const cardComponent =
|
||||
this.engine.card.find(childCard);
|
||||
if (
|
||||
(cardComponent?.constructor as CardEntry)
|
||||
.lazyRender
|
||||
!childLoading ||
|
||||
childCard[0]['__card_rendered']
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -271,6 +271,10 @@ export type RepairOp = TargetOp & {
|
|||
};
|
||||
|
||||
export interface MutationInterface extends EventEmitter2 {
|
||||
/**
|
||||
* 是否终止中
|
||||
*/
|
||||
isStopped: boolean;
|
||||
/**
|
||||
* 设置文档对象 OT 文档对象,或自定义文档对象
|
||||
* @param doc 文档对象
|
||||
|
@ -471,6 +475,10 @@ export interface OTInterface extends EventEmitter2 {
|
|||
* 停止监听DOM树改变
|
||||
*/
|
||||
stopMutation(): void;
|
||||
/**
|
||||
* 是否终止中
|
||||
*/
|
||||
isStopped(): boolean;
|
||||
/**
|
||||
* 开始缓存操作,开启后将拦截监听并缓存起来
|
||||
*/
|
||||
|
|
|
@ -143,7 +143,7 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
|
|||
|
||||
update(mode: string, code?: string) {
|
||||
this.mode = mode;
|
||||
if (code) {
|
||||
if (code !== undefined) {
|
||||
this.codeMirror?.setValue(code);
|
||||
}
|
||||
this.codeMirror?.setOption('mode', this.getSyntax(mode));
|
||||
|
@ -151,7 +151,7 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
|
|||
'readOnly',
|
||||
!isEngine(this.editor) || this.editor.readonly ? true : false,
|
||||
);
|
||||
if (!code) this.save();
|
||||
if (code !== undefined) this.save();
|
||||
}
|
||||
|
||||
render(mode: string, value: string) {
|
||||
|
|
|
@ -130,7 +130,11 @@ class CodeBlcok extends Card<CodeBlockValue> {
|
|||
|
||||
render() {
|
||||
if (!this.codeEditor) return;
|
||||
if (!this.mirror) this.getCenter().append(this.codeEditor.container);
|
||||
if (!this.codeEditor.container.inEditor()) {
|
||||
this.codeEditor.container = $(this.codeEditor.renderTemplate());
|
||||
this.mirror = undefined;
|
||||
this.getCenter().append(this.codeEditor.container);
|
||||
}
|
||||
const value = this.getValue();
|
||||
|
||||
const mode = value?.mode || 'plain';
|
||||
|
|
|
@ -144,7 +144,7 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
|
|||
|
||||
update(mode: string, code?: string) {
|
||||
this.mode = mode;
|
||||
if (code) {
|
||||
if (code !== undefined) {
|
||||
this.codeMirror?.setValue(code);
|
||||
}
|
||||
this.codeMirror?.setOption('mode', this.getSyntax(mode));
|
||||
|
@ -152,7 +152,7 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
|
|||
'readOnly',
|
||||
!isEngine(this.editor) || this.editor.readonly ? true : false,
|
||||
);
|
||||
if (!code) this.save();
|
||||
if (code !== undefined) this.save();
|
||||
}
|
||||
|
||||
render(mode: string, value: string) {
|
||||
|
|
|
@ -127,7 +127,11 @@ class CodeBlcok extends Card<CodeBlockValue> {
|
|||
|
||||
render() {
|
||||
if (!this.codeEditor) return;
|
||||
if (!this.mirror) this.getCenter().append(this.codeEditor.container);
|
||||
if (!this.codeEditor.container.inEditor()) {
|
||||
this.codeEditor.container = $(this.codeEditor.renderTemplate());
|
||||
this.mirror = undefined;
|
||||
this.getCenter().append(this.codeEditor.container);
|
||||
}
|
||||
const value = this.getValue();
|
||||
|
||||
const mode = value?.mode || 'plain';
|
||||
|
|
|
@ -241,7 +241,7 @@ export default class FileCard extends Card<FileValue> {
|
|||
render(): string | void | NodeInterface {
|
||||
const value = this.getValue();
|
||||
if (!value) return;
|
||||
if (!this.container) {
|
||||
if (!this.container || this.container.length === 0) {
|
||||
this.container = $(this.renderTemplate(value));
|
||||
this.getCenter().empty().append(this.container);
|
||||
} else {
|
||||
|
|
|
@ -248,7 +248,7 @@ class ImageComponent extends Card<ImageValue> {
|
|||
render(loadingBg?: string): string | void | NodeInterface {
|
||||
const value = this.getValue();
|
||||
if (!value) return;
|
||||
if (!this.image) {
|
||||
if (!this.image || this.image.root.length === 0) {
|
||||
this.image = new Image(this.editor, {
|
||||
root: this.root,
|
||||
container: this.getCenter(),
|
||||
|
|
|
@ -633,7 +633,11 @@ class TableComponent extends Card<TableValue> implements TableInterface {
|
|||
Template.isReadonly = !isEngine(this.editor) || this.editor.readonly;
|
||||
const value = this.getValue();
|
||||
// 重新渲染
|
||||
if (this.wrapper) {
|
||||
if (
|
||||
this.wrapper &&
|
||||
this.wrapper.length > 0 &&
|
||||
!!this.wrapper[0].parentNode
|
||||
) {
|
||||
// 重新绘制列头部和行头部
|
||||
const colsHeader = this.wrapper.find(Template.COLS_HEADER_CLASS);
|
||||
if (value?.cols) {
|
||||
|
|
Loading…
Reference in New Issue