fix: 懒加载会导致协同错误

This commit is contained in:
yanmao 2021-12-17 21:07:22 +08:00
parent db7105a28e
commit 919e7cac8c
14 changed files with 105 additions and 63 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
/**
*
*/

View File

@ -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) {

View File

@ -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';

View File

@ -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) {

View File

@ -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';

View File

@ -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 {

View File

@ -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(),

View File

@ -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) {