Skip to content

Commit

Permalink
Merge pull request #1365 from alibaba/native-fragment
Browse files Browse the repository at this point in the history
chore: empty fragment diff optimize
  • Loading branch information
yuanyan authored Sep 17, 2019
2 parents 4fccb60 + 894e44e commit a2107f3
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 38 deletions.
3 changes: 2 additions & 1 deletion packages/rax/src/vdom/composite.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,8 @@ class CompositeComponent extends BaseComponent {
let lastNativeNode = null;
let prevNativeNode = prevRenderedComponent.__getNativeNode();
// Only prevNativeNode is empty fragment should find the prevSlibingNativeNode
if (isArray(prevNativeNode) && prevNativeNode.length === 0) {
// And current root component is fragment, but not need find the prevSlibingNativeNode when init mounting
if (isArray(prevNativeNode) && prevNativeNode.length === 0 && instance.__rootID == null) {
lastNativeNode = getPrevSiblingNativeNode(prevRenderedComponent);
}

Expand Down
18 changes: 14 additions & 4 deletions packages/rax/src/vdom/getPrevSiblingNativeNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Host from './host';
import toArray from '../toArray';
import { isArray } from '../types';
import { INTERNAL } from '../constant';

/**
Expand All @@ -19,12 +19,22 @@ export default function getPrevSiblingNativeNode(component) {
}

const keys = Object.keys(parent._renderedChildren);
// Find previous sibling native node from current mount index
for (let i = component.__mountIndex - 1; i >= 0; i--) {
const nativeNode = toArray(parent._renderedChildren[keys[i]].__getNativeNode());
if (nativeNode.length > 0) {
return nativeNode[nativeNode.length - 1];
const nativeNode = parent._renderedChildren[keys[i]].__getNativeNode();
// Fragment component always return array
if (isArray(nativeNode)) {
if (nativeNode.length > 0) {
// Get the last one
return nativeNode[nativeNode.length - 1];
}
} else {
// Others maybe native node or empty node
return nativeNode;
}
}

// Find parent over parent
if (parent instanceof Host.Fragment) {
component = parent;
} else {
Expand Down
14 changes: 7 additions & 7 deletions packages/rax/src/vdom/instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export default {
if (!node[KEY]) {
node[KEY] = instance;
// Record root instance to roots map
if (instance.rootID) {
Host.rootInstances[instance.rootID] = instance;
Host.rootComponents[instance.rootID] = instance[INTERNAL];
if (instance.__rootID) {
Host.rootInstances[instance.__rootID] = instance;
Host.rootComponents[instance.__rootID] = instance[INTERNAL];
}
}
},
Expand All @@ -28,9 +28,9 @@ export default {
let instance = this.get(node);
if (instance) {
node[KEY] = null;
if (instance.rootID) {
delete Host.rootComponents[instance.rootID];
delete Host.rootInstances[instance.rootID];
if (instance.__rootID) {
delete Host.rootComponents[instance.__rootID];
delete Host.rootInstances[instance.__rootID];
}
}
},
Expand Down Expand Up @@ -64,7 +64,7 @@ export default {

// Update root component
let prevRootInstance = this.get(container);
if (prevRootInstance && prevRootInstance.rootID) {
if (prevRootInstance && prevRootInstance.__rootID) {
if (parentContext) {
// Using __penddingContext to pass new context
prevRootInstance[INTERNAL].__penddingContext = parentContext;
Expand Down
48 changes: 26 additions & 22 deletions packages/rax/src/vdom/native.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,13 @@ export default class NativeComponent extends BaseComponent {
}
}

let prevFirstChild;
let prevFirstNativeNode;
let shouldUnmountPrevFirstChild;
let parent = this.__getNativeNode();
let isFragmentParent = isArray(parent);
let prevFirstChild = null;
let prevFirstNativeNode = null;
let isPrevFirstEmptyFragment = false;
let shouldUnmountPrevFirstChild = false;
let lastPlacedNode = null;

// Directly remove all children from component, if nextChildren is empty (null, [], '').
// `driver.removeChildren` is optional driver protocol.
Expand All @@ -361,47 +365,49 @@ export default class NativeComponent extends BaseComponent {
prevFirstNativeNode = prevFirstChild.__getNativeNode();

if (isArray(prevFirstNativeNode)) {
isPrevFirstEmptyFragment = prevFirstNativeNode.length === 0;
prevFirstNativeNode = prevFirstNativeNode[0];
}
} else if (shouldUnmount) {
prevChild.unmountComponent(shouldRemoveAllChildren);
}
}

// 1. When fragment embed fragment updated but prev fragment is empty
// that need to get the prev sibling native node.
// like: [ [] ] -> [ [1, 2] ]
// 2. When prev fragment is empty and update to other type
// like: [ [], 1 ] -> [ 1, 2 ]
if (isFragmentParent && parent.length === 0 || isPrevFirstEmptyFragment) {
lastPlacedNode = getPrevSiblingNativeNode(this);
}
}


if (nextChildren != null) {
// `nextIndex` will increment for each child in `nextChildren`
let nextIndex = 0;
let lastPlacedNode = null;
let nextNativeNodes = [];
let isFragmentAsParent = false;
let insertNodes = (nativeNodes, parent) => {

function insertNodes(nativeNodes, parentNode) {
// The nativeNodes maybe fragment, so convert to array type
nativeNodes = toArray(nativeNodes);
let prevSiblingNativeNode;

// Only parent is fragment need to get the prev sibling node
if (isFragmentAsParent) {
prevSiblingNativeNode = getPrevSiblingNativeNode(this);
}

for (let i = 0, l = nativeNodes.length; i < l; i++) {
if (lastPlacedNode) {
// Should reverse order when insert new child after lastPlacedNode:
// [lastPlacedNode, *newChild1, *newChild2]
// [lastPlacedNode, *newChild1, *newChild2],
// And if prev is empty fragment, lastPlacedNode is the prevSiblingNativeNode found.
driver.insertAfter(nativeNodes[l - i - 1], lastPlacedNode);
} else if (prevFirstNativeNode) {
// [*newChild1, *newChild2, prevFirstNativeNode]
driver.insertBefore(nativeNodes[i], prevFirstNativeNode);
} else if (prevSiblingNativeNode) {
// If parent is fragment, find nativeNode previous sibling node
driver.insertAfter(nativeNodes[i], prevSiblingNativeNode);
} else if (parent) {
} else if (parentNode) {
// [*newChild1, *newChild2]
driver.appendChild(nativeNodes[i], parent);
driver.appendChild(nativeNodes[i], parentNode);
}
}
};
}

for (let name in nextChildren) {
let nextChild = nextChildren[name];
Expand All @@ -417,10 +423,8 @@ export default class NativeComponent extends BaseComponent {
} else {
// Mount nextChild that in prevChildren there has no some name

let parent = this.__getNativeNode();
// Fragment extended native component, so if parent is fragment should get this._parent
if (isArray(parent)) {
isFragmentAsParent = true;
if (isFragmentParent) {
parent = this._parent;
}

Expand Down
8 changes: 4 additions & 4 deletions packages/rax/src/vdom/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class Root extends Component {
constructor() {
super();
// Using fragment instead of null for avoid create a comment node when init mount
this.element = [];
this.rootID = rootID++;
this.__element = [];
this.__rootID = rootID++;
}

__getPublicInstance() {
Expand All @@ -20,12 +20,12 @@ class Root extends Component {
}

update(element) {
this.element = element;
this.__element = element;
this.forceUpdate();
}

render() {
return this.element;
return this.__element;
}
}

Expand Down

0 comments on commit a2107f3

Please sign in to comment.