Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: empty fragment diff optimize #1365

Merged
merged 4 commits into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isPrevFirstEmptyFragment这个变量感觉稍微有点无必要, 直接判断 prevFirstNativeNode == null 就行了?

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