Merge branch 'fix/lihao-bugs-200902' into 'release/0.9.17'

refactor: 重新梳理 purge / remove / delete 的职责,以及相应重构

refactor: 调整判断 fieldId 是否存在的逻辑
fix: 解决 redo 时页面节点数不对以及 fieldId 被重置

See merge request !960745
This commit is contained in:
高凯 2020-09-08 10:44:03 +08:00
commit 67f34ea3b4
6 changed files with 123 additions and 100 deletions

View File

@ -267,14 +267,16 @@ export class DocumentModel {
/** /**
* *
*/ */
internalRemoveAndPurgeNode(node: Node) { internalRemoveAndPurgeNode(node: Node, useMutator = false) {
if (!this.nodes.has(node)) { if (!this.nodes.has(node)) {
return; return;
} }
this._nodesMap.delete(node.id); node.remove(useMutator);
}
unlinkNode(node: Node) {
this.nodes.delete(node); this.nodes.delete(node);
this.selection.remove(node.id); this._nodesMap.delete(node.id);
node.remove();
} }
@obx.ref private _dropLocation: DropLocation | null = null; @obx.ref private _dropLocation: DropLocation | null = null;
@ -318,20 +320,20 @@ export class DocumentModel {
* schema * schema
*/ */
get schema(): RootSchema { get schema(): RootSchema {
return this.rootNode.schema as any; return this.rootNode?.schema as any;
} }
import(schema: RootSchema, checkId = false) { import(schema: RootSchema, checkId = false) {
// TODO: do purge
this.nodes.forEach(node => { this.nodes.forEach(node => {
this.internalRemoveAndPurgeNode(node, true);
this.destroyNode(node); this.destroyNode(node);
}); });
this.rootNode.import(schema as any, checkId); this.rootNode?.import(schema as any, checkId);
// todo: select added and active track added // todo: select added and active track added
} }
export(stage: TransformStage = TransformStage.Serilize) { export(stage: TransformStage = TransformStage.Serilize) {
return this.rootNode.export(stage); return this.rootNode?.export(stage);
} }
/** /**

View File

@ -16,7 +16,7 @@ export class NodeChildren {
} }
interalInitParent() { internalInitParent() {
this.children.forEach(child => child.internalSetParent(this.owner)); this.children.forEach(child => child.internalSetParent(this.owner));
} }
@ -56,7 +56,7 @@ export class NodeChildren {
} }
this.children = children; this.children = children;
this.interalInitParent(); this.internalInitParent();
if (!shallowEqual(children, originChildren)) { if (!shallowEqual(children, originChildren)) {
this.emitter.emit('change'); this.emitter.emit('change');
} }
@ -92,20 +92,53 @@ export class NodeChildren {
return this.children.length; return this.children.length;
} }
private purged = false;
/** /**
* *
*/ */
delete(node: Node, purge = false, useMutator = true): boolean { purge(useMutator = true) {
if (this.purged) {
return;
}
this.purged = true;
this.children.forEach((child) => {
child.purge(useMutator);
});
}
unlinkChild(node: Node) {
const i = this.children.indexOf(node); const i = this.children.indexOf(node);
if (i < 0) { if (i < 0) {
return false; return false;
} }
const deleted = this.children.splice(i, 1)[0]; this.children.splice(i, 1);
}
/**
*
*/
delete(node: Node, purge = false, useMutator = true): boolean {
if (node.isParental()) {
node.children.forEach(subNode => {
subNode.remove(useMutator, purge);
});
}
if (purge) { if (purge) {
// should set parent null // should set parent null
deleted.internalSetParent(null, useMutator); node.internalSetParent(null, useMutator);
deleted.purge(useMutator); try {
node.purge(useMutator);
} catch(err) {
console.error(err);
} }
}
const i = this.children.indexOf(node);
if (i < 0) {
return false;
}
this.children.splice(i, 1);
const document = node.document;
document.unlinkNode(node);
document.selection.remove(node.id);
this.emitter.emit('change'); this.emitter.emit('change');
if (useMutator) { if (useMutator) {
this.reportModified(node, this.owner, {type: 'remove', removeIndex: i, removeNode: node}); this.reportModified(node, this.owner, {type: 'remove', removeIndex: i, removeNode: node});
@ -294,18 +327,6 @@ export class NodeChildren {
}; };
} }
private purged = false;
/**
*
*/
purge(useMutator = true) {
if (this.purged) {
return;
}
this.purged = true;
this.children.forEach((child) => child.purge(useMutator));
}
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
// 保证向前兼容性 // 保证向前兼容性
return 'Array'; return 'Array';
@ -331,7 +352,7 @@ export class NodeChildren {
try { try {
callbacks?.onSubtreeModified.call(node, owner, options); callbacks?.onSubtreeModified.call(node, owner, options);
} catch (e) { } catch (e) {
console.error('error when excute experimental.callbacks.onNodeAdd', e); console.error('error when excute experimental.callbacks.onSubtreeModified', e);
} }
} }

View File

@ -155,9 +155,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
children: isDOMText(children) || isJSExpression(children) ? children : '', children: isDOMText(children) || isJSExpression(children) ? children : '',
}); });
} else { } else {
// 这里 props 被初始化两次,一次 new一次 importnew 的实例需要给 propsReducer 的钩子去使用,
// import 是为了使用钩子返回的值,并非完全幂等的操作,部分行为执行两次会有 bug
// 所以在 props 里会对 new / import 做一些区别化的解析
this.props = new Props(this, props, extras); this.props = new Props(this, props, extras);
this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children)); this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children));
this._children.interalInitParent(); this._children.internalInitParent();
this.props.import(this.upgradeProps(this.initProps(props || {})), this.upgradeProps(extras || {})); this.props.import(this.upgradeProps(this.initProps(props || {})), this.upgradeProps(extras || {}));
this.setupAutoruns(); this.setupAutoruns();
} }
@ -258,14 +261,15 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return; return;
} }
// 解除老的父子关系,但不需要真的删除节点
if (this._parent) { if (this._parent) {
if (this.isSlot()) { if (this.isSlot()) {
this._parent.removeSlot(this, false); this._parent.unlinkSlot(this);
} else { } else {
this._parent.children.delete(this, false, useMutator); this._parent.children.unlinkChild(this);
} }
} }
// 建立新的父子关系
this._parent = parent; this._parent = parent;
if (parent) { if (parent) {
this.document.removeWillPurge(this); this.document.removeWillPurge(this);
@ -298,12 +302,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
/** /**
* *
*/ */
remove(useMutator = true) { remove(useMutator = true, purge = true) {
if (this.parent) { if (this.parent) {
if (this.isSlot()) { if (this.isSlot()) {
this.parent.removeSlot(this, true); this.parent.removeSlot(this, purge);
} else { } else {
this.parent.children.delete(this, true, useMutator); this.parent.children.delete(this, purge, useMutator);
} }
} }
} }
@ -645,6 +649,14 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return comparePosition(this, otherNode); return comparePosition(this, otherNode);
} }
unlinkSlot(slotNode: Node) {
const i = this._slots.indexOf(slotNode);
if (i < 0) {
return false;
}
this._slots.splice(i, 1);
}
/** /**
* Slot节点 * Slot节点
*/ */
@ -653,12 +665,14 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (i < 0) { if (i < 0) {
return false; return false;
} }
const deleted = this._slots.splice(i, 1)[0];
if (purge) { if (purge) {
// should set parent null // should set parent null
deleted.internalSetParent(null); slotNode.internalSetParent(null, false);
deleted.purge(); slotNode.purge();
} }
this.document.unlinkNode(slotNode);
this.document.selection.remove(slotNode.id);
this._slots.splice(i, 1);
return false; return false;
} }
@ -699,20 +713,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (this.purged) { if (this.purged) {
return; return;
} }
// if (this._parent) {
// // should remove thisNode before purge
// this.remove(useMutator);
// return;
// }
this.purged = true; this.purged = true;
if (this.isParental()) {
this.children.purge(useMutator);
}
this.autoruns?.forEach((dispose) => dispose()); this.autoruns?.forEach((dispose) => dispose());
this.props.purge(); this.props.purge();
this.document.internalRemoveAndPurgeNode(this);
this.document.destroyNode(this); this.document.destroyNode(this);
this.remove(useMutator);
} }
/** /**

View File

@ -22,6 +22,37 @@ export class Prop implements IPropParent {
readonly isProp = true; readonly isProp = true;
readonly owner: Node; readonly owner: Node;
private stash: PropStash | undefined;
/**
*
*/
@obx key: string | number | undefined;
/**
*
*/
@obx spread: boolean;
readonly props: Props;
readonly options: any;
constructor(
public parent: IPropParent,
value: CompositeValue | UNSET = UNSET,
key?: string | number,
spread = false,
options = {},
) {
this.owner = parent.owner;
this.props = parent.props;
this.key = key;
this.spread = spread;
this.options = options;
if (value !== UNSET) {
this.setValue(value);
}
}
/** /**
* @see SettingTarget * @see SettingTarget
*/ */
@ -197,7 +228,7 @@ export class Prop implements IPropParent {
} else if (Array.isArray(val)) { } else if (Array.isArray(val)) {
this._type = 'list'; this._type = 'list';
} else if (isPlainObject(val)) { } else if (isPlainObject(val)) {
if (isJSSlot(val)) { if (isJSSlot(val) && this.options.propsMode !== 'init') {
this.setAsSlot(val); this.setAsSlot(val);
return; return;
} }
@ -347,34 +378,6 @@ export class Prop implements IPropParent {
return this._maps; return this._maps;
} }
private stash: PropStash | undefined;
/**
*
*/
@obx key: string | number | undefined;
/**
*
*/
@obx spread: boolean;
readonly props: Props;
constructor(
public parent: IPropParent,
value: CompositeValue | UNSET = UNSET,
key?: string | number,
spread = false,
) {
this.owner = parent.owner;
this.props = parent.props;
if (value !== UNSET) {
this.setValue(value);
}
this.key = key;
this.spread = spread;
}
/** /**
* *
* @param stash * @param stash

View File

@ -57,9 +57,9 @@ export class Props implements IPropParent {
constructor(readonly owner: Node, value?: PropsMap | PropsList | null, extras?: object) { constructor(readonly owner: Node, value?: PropsMap | PropsList | null, extras?: object) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
this.type = 'list'; this.type = 'list';
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread)); this.items = value.map(item => new Prop(this, item.value, item.name, item.spread, { propsMode: 'init' }));
} else if (value != null) { } else if (value != null) {
this.items = Object.keys(value).map(key => new Prop(this, value[key], key)); this.items = Object.keys(value).map(key => new Prop(this, value[key], key, false, { propsMode: 'init' }));
} }
if (extras) { if (extras) {
Object.keys(extras).forEach(key => { Object.keys(extras).forEach(key => {

View File

@ -26,21 +26,10 @@ export const designer = new Designer({ editor: editor });
editor.set(Designer, designer); editor.set(Designer, designer);
editor.set('designer', designer); editor.set('designer', designer);
let nodeCache: any = {};
designer.project.onCurrentDocumentChange((doc) => { designer.project.onCurrentDocumentChange((doc) => {
nodeCache = {};
doc.nodesMap.forEach((node) => {
nodeCache[node.id] = node;
});
doc.onRendererReady(() => { doc.onRendererReady(() => {
bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY); bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY);
}); });
doc.onNodeCreate((node) => {
nodeCache[node.id] = node;
});
doc.onNodeDestroy((node) => {
delete nodeCache[node.id];
});
}); });
interface Variable { interface Variable {
@ -89,6 +78,18 @@ function upgradePropsReducer(props: any) {
// 升级 Props // 升级 Props
designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade); designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade);
function getCurrentFieldIds() {
const fieldIds: any = [];
const nodesMap = designer?.currentDocument?.nodesMap || new Map();
nodesMap.forEach((curNode: any) => {
const fieldId = nodesMap?.get(curNode.id)?.getPropValue('fieldId');
if (fieldId) {
fieldIds.push(fieldId);
}
});
return fieldIds;
}
// 节点 props 初始化 // 节点 props 初始化
designer.addPropsReducer((props, node) => { designer.addPropsReducer((props, node) => {
// run initials // run initials
@ -96,16 +97,8 @@ designer.addPropsReducer((props, node) => {
...props, ...props,
}; };
if (newProps.fieldId) { if (newProps.fieldId) {
const fieldIds: any = []; const fieldIds = getCurrentFieldIds();
Object.keys(nodeCache).forEach(nodeId => {
if (nodeId === node.id) {
return;
}
const fieldId = nodeCache[nodeId].getPropValue('fieldId');
if (fieldId) {
fieldIds.push(fieldId);
}
});
// 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现 // 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现
if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) { if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) {
newProps.fieldId = undefined; newProps.fieldId = undefined;