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)) {
return;
}
this._nodesMap.delete(node.id);
node.remove(useMutator);
}
unlinkNode(node: Node) {
this.nodes.delete(node);
this.selection.remove(node.id);
node.remove();
this._nodesMap.delete(node.id);
}
@obx.ref private _dropLocation: DropLocation | null = null;
@ -318,20 +320,20 @@ export class DocumentModel {
* schema
*/
get schema(): RootSchema {
return this.rootNode.schema as any;
return this.rootNode?.schema as any;
}
import(schema: RootSchema, checkId = false) {
// TODO: do purge
this.nodes.forEach(node => {
this.internalRemoveAndPurgeNode(node, true);
this.destroyNode(node);
});
this.rootNode.import(schema as any, checkId);
this.rootNode?.import(schema as any, checkId);
// todo: select added and active track added
}
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));
}
@ -56,7 +56,7 @@ export class NodeChildren {
}
this.children = children;
this.interalInitParent();
this.internalInitParent();
if (!shallowEqual(children, originChildren)) {
this.emitter.emit('change');
}
@ -92,20 +92,53 @@ export class NodeChildren {
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);
if (i < 0) {
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) {
// should set parent null
deleted.internalSetParent(null, useMutator);
deleted.purge(useMutator);
node.internalSetParent(null, 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');
if (useMutator) {
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]() {
// 保证向前兼容性
return 'Array';
@ -331,7 +352,7 @@ export class NodeChildren {
try {
callbacks?.onSubtreeModified.call(node, owner, options);
} 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 : '',
});
} else {
// 这里 props 被初始化两次,一次 new一次 importnew 的实例需要给 propsReducer 的钩子去使用,
// import 是为了使用钩子返回的值,并非完全幂等的操作,部分行为执行两次会有 bug
// 所以在 props 里会对 new / import 做一些区别化的解析
this.props = new Props(this, props, extras);
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.setupAutoruns();
}
@ -258,14 +261,15 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return;
}
// 解除老的父子关系,但不需要真的删除节点
if (this._parent) {
if (this.isSlot()) {
this._parent.removeSlot(this, false);
this._parent.unlinkSlot(this);
} else {
this._parent.children.delete(this, false, useMutator);
this._parent.children.unlinkChild(this);
}
}
// 建立新的父子关系
this._parent = parent;
if (parent) {
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.isSlot()) {
this.parent.removeSlot(this, true);
this.parent.removeSlot(this, purge);
} 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);
}
unlinkSlot(slotNode: Node) {
const i = this._slots.indexOf(slotNode);
if (i < 0) {
return false;
}
this._slots.splice(i, 1);
}
/**
* Slot节点
*/
@ -653,12 +665,14 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (i < 0) {
return false;
}
const deleted = this._slots.splice(i, 1)[0];
if (purge) {
// should set parent null
deleted.internalSetParent(null);
deleted.purge();
slotNode.internalSetParent(null, false);
slotNode.purge();
}
this.document.unlinkNode(slotNode);
this.document.selection.remove(slotNode.id);
this._slots.splice(i, 1);
return false;
}
@ -699,20 +713,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (this.purged) {
return;
}
// if (this._parent) {
// // should remove thisNode before purge
// this.remove(useMutator);
// return;
// }
this.purged = true;
if (this.isParental()) {
this.children.purge(useMutator);
}
this.autoruns?.forEach((dispose) => dispose());
this.props.purge();
this.document.internalRemoveAndPurgeNode(this);
this.document.destroyNode(this);
this.remove(useMutator);
}
/**

View File

@ -22,6 +22,37 @@ export class Prop implements IPropParent {
readonly isProp = true;
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
*/
@ -197,7 +228,7 @@ export class Prop implements IPropParent {
} else if (Array.isArray(val)) {
this._type = 'list';
} else if (isPlainObject(val)) {
if (isJSSlot(val)) {
if (isJSSlot(val) && this.options.propsMode !== 'init') {
this.setAsSlot(val);
return;
}
@ -347,34 +378,6 @@ export class Prop implements IPropParent {
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

View File

@ -57,9 +57,9 @@ export class Props implements IPropParent {
constructor(readonly owner: Node, value?: PropsMap | PropsList | null, extras?: object) {
if (Array.isArray(value)) {
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) {
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) {
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);
let nodeCache: any = {};
designer.project.onCurrentDocumentChange((doc) => {
nodeCache = {};
doc.nodesMap.forEach((node) => {
nodeCache[node.id] = node;
});
doc.onRendererReady(() => {
bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY);
});
doc.onNodeCreate((node) => {
nodeCache[node.id] = node;
});
doc.onNodeDestroy((node) => {
delete nodeCache[node.id];
});
});
interface Variable {
@ -89,6 +78,18 @@ function upgradePropsReducer(props: any) {
// 升级 Props
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 初始化
designer.addPropsReducer((props, node) => {
// run initials
@ -96,16 +97,8 @@ designer.addPropsReducer((props, node) => {
...props,
};
if (newProps.fieldId) {
const fieldIds: any = [];
Object.keys(nodeCache).forEach(nodeId => {
if (nodeId === node.id) {
return;
}
const fieldId = nodeCache[nodeId].getPropValue('fieldId');
if (fieldId) {
fieldIds.push(fieldId);
}
});
const fieldIds = getCurrentFieldIds();
// 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现
if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) {
newProps.fieldId = undefined;