feat(react,vue): improve managing node performance

This commit is contained in:
zoomchan-cxj
2021-11-18 13:02:30 +08:00
parent 082329e591
commit 8b35ba6bdb
9 changed files with 100 additions and 253 deletions
+13 -3
View File
@@ -6,7 +6,7 @@
# 事件监听器
这里是向前端发送一个名叫 rotate 的事件,里面有个参数是 result,这样就发送到前端去了。
这里监听 rotate 的事件,里面有回调参数 result。
```js
// 将入口文件中 setApp() 时保存的 Vue 实例取出来。
@@ -18,7 +18,7 @@ export default {
console.log(rsp.result);
}
},
didMount() {
mounted() {
// 通过 app 监听 rotate 事件,并通过 this.listener 在事件发生时触发回调。
app.$on('rotate', this.listener);
}
@@ -26,11 +26,21 @@ export default {
```
# 事件触发
如果需要手动发送事件,可以通过 `app.$emit` 触发。
```js
const app = getApp();
app.$emit('rotate', { width: 100, height: 100 });
```
# 事件卸载
如果不需要使用的时候记得调用一下移除监听的方法,一般放在组件的卸载生命周期中执行。
```jsx
```js
const app = getApp();
app.$off('rotate', this.listener);
```
+29 -105
View File
@@ -42,10 +42,6 @@ class ViewNode {
public parentNode: ViewNode | null = null;
public prevSibling: ViewNode | null = null;
public nextSibling: ViewNode | null = null;
constructor() {
// Virtual DOM node id, will used in native to identify.
this.nodeId = getNodeId();
@@ -56,16 +52,6 @@ class ViewNode {
return this.constructor.name;
}
get firstChild() {
return this.childNodes.length ? this.childNodes[0] : null;
}
get lastChild() {
return this.childNodes.length
? this.childNodes[this.childNodes.length - 1]
: null;
}
get isMounted() {
return this.mounted;
}
@@ -79,104 +65,66 @@ class ViewNode {
if (!childNode) {
throw new Error('Can\'t insert child.');
}
if (childNode.meta.skipAddToDom) {
return;
}
if (!referenceNode) {
return this.appendChild(childNode);
}
if (referenceNode.parentNode !== this) {
throw new Error('Can\'t insert child, because the reference node has a different parent.');
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t insert child, because it already has a different parent.');
}
const index = this.childNodes.indexOf(referenceNode);
childNode.parentNode = this;
childNode.nextSibling = referenceNode;
childNode.prevSibling = this.childNodes[index - 1];
referenceNode.prevSibling = childNode;
this.childNodes.splice(index, 0, childNode);
return insertChild(this, childNode, index);
}
moveChild(childNode: ViewNode, referenceNode: ViewNode) {
if (!childNode) {
throw new Error('Can\'t mvoe child.');
throw new Error('Can\'t move child.');
}
if (childNode.meta.skipAddToDom) {
return;
}
if (!referenceNode) {
return this.appendChild(childNode);
}
if (referenceNode.parentNode !== this) {
throw new Error('Can\'t move child, because the reference node has a different parent.');
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t move child, because it already has a different parent.');
}
const oldIndex = this.childNodes.indexOf(childNode);
const newIndex = this.childNodes.indexOf(referenceNode);
const referenceIndex = this.childNodes.indexOf(referenceNode);
// return if the moved index is the same as the previous one
if (newIndex === oldIndex) {
if (referenceIndex === oldIndex) {
return childNode;
}
// set new siblings relations
childNode.nextSibling = referenceNode;
childNode.prevSibling = referenceNode.prevSibling;
referenceNode.prevSibling = childNode;
if (this.childNodes[newIndex - 1]) {
this.childNodes[newIndex - 1].nextSibling = childNode;
}
if (this.childNodes[newIndex + 1]) {
this.childNodes[newIndex + 1].prevSibling = childNode;
}
if (this.childNodes[oldIndex - 1]) {
this.childNodes[oldIndex - 1].nextSibling = this.childNodes[oldIndex + 1];
}
if (this.childNodes[oldIndex + 1]) {
this.childNodes[oldIndex + 1].prevSibling = this.childNodes[oldIndex - 1];
}
// remove old child node from native
removeChild(this, childNode);
// remove old child and insert new child, which is like moving child
this.childNodes.splice(oldIndex, 1);
removeChild(this, childNode, oldIndex);
const newIndex = this.childNodes.indexOf(referenceNode);
this.childNodes.splice(newIndex, 0, childNode);
this.childNodes.splice(oldIndex + (newIndex < oldIndex ? 1 : 0), 1);
// should filter empty nodes before finding the index of node
const atIndex = this.childNodes.filter(ch => ch.index > -1).indexOf(childNode);
return insertChild(this, childNode, atIndex);
return insertChild(this, childNode, newIndex);
}
appendChild(childNode: ViewNode) {
if (!childNode) {
throw new Error('Can\'t append child.');
}
if (childNode.meta.skipAddToDom) {
return;
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t append child, because it already has a different parent.');
}
childNode.parentNode = this;
if (this.lastChild) {
childNode.prevSibling = this.lastChild;
this.lastChild.nextSibling = childNode;
}
this.childNodes.push(childNode);
insertChild(this, childNode, this.childNodes.length - 1);
}
@@ -184,36 +132,18 @@ class ViewNode {
if (!childNode) {
throw new Error('Can\'t remove child.');
}
if (!childNode.parentNode) {
throw new Error('Can\'t remove child, because it has no parent.');
}
if (childNode.parentNode !== this) {
throw new Error('Can\'t remove child, because it has a different parent.');
}
if (childNode.meta.skipAddToDom) {
return;
}
removeChild(this, childNode);
// FIXME: parentNode should be null when removeChild, But it breaks add the node again.
// Issue position: https://github.com/vuejs/vue/tree/master/src/core/vdom/patch.js#L250
// childNode.parentNode = null;
if (childNode.prevSibling) {
childNode.prevSibling.nextSibling = childNode.nextSibling;
childNode.prevSibling = null;
if (!childNode.parentNode) {
throw new Error('Can\'t remove child, because it has no parent.');
}
if (childNode.nextSibling) {
childNode.nextSibling.prevSibling = childNode.prevSibling;
childNode.nextSibling = null;
if (childNode.parentNode !== this) {
throw new Error('Can\'t remove child, because it has a different parent.');
}
this.childNodes = this.childNodes.filter(node => node !== childNode);
const index = this.childNodes.indexOf(childNode);
this.childNodes.splice(index, 1);
removeChild(this, childNode, index);
}
/**
@@ -238,22 +168,16 @@ class ViewNode {
/**
* Traverse the children and execute callback
* @param callback - callback function
* @param newIndex - index to be updated
*/
traverseChildren(callback: Function) {
// Find the index and apply callback
let index;
if (this.parentNode) {
index = this.parentNode.childNodes.filter(node => !node.meta.skipAddToDom).indexOf(this);
} else {
index = 0;
}
this.index = index;
traverseChildren(callback: Function, newIndex: number | undefined = 0) {
this.index = !this.parentNode ? 0 : newIndex;
callback(this);
// Find the children
if (this.childNodes.length) {
this.childNodes.forEach((childNode) => {
this.traverseChildren.call(childNode, callback);
this.childNodes.forEach((childNode, index) => {
this.traverseChildren.call(childNode, callback, index);
});
}
}
+1 -1
View File
@@ -105,7 +105,7 @@ class HippyReact implements HippyReact {
const { __instanceId__: rootViewId } = superProps;
trace(...componentName, 'Start', appName, 'with rootViewId', rootViewId, superProps);
// Update nodeId for contaienr
// Update nodeId for container
this.rootContainer.containerInfo.nodeId = rootViewId;
if (silent) {
setSilent(silent);
@@ -59,6 +59,7 @@ function createInstance(
}
}
});
// only HostComponent(5) or Fragment(7) render to native
if ([5, 7].indexOf(workInProgress.tag) < 0) {
element.meta.skipAddToDom = true;
}
@@ -71,7 +72,6 @@ function createTextInstance(newText: string, rootContainerInstance: Document) {
element.meta = {
component: {
name: 'Text',
skipAddToDom: true,
},
};
return element;
+18 -20
View File
@@ -113,7 +113,7 @@ function getNativeProps(node: Element) {
}
/**
* Get target node attributes, use to chrome devTool tag attribute show while debugging
* Get target node attributes, used to chrome devTool tag attribute show while debugging
*/
function getTargetNodeAttributes(targetNode: Element) {
try {
@@ -124,7 +124,6 @@ function getTargetNodeAttributes(targetNode: Element) {
};
delete attributes.text;
delete attributes.value;
return attributes;
} catch (e) {
warn('getTargetNodeAttributes error:', e);
@@ -140,14 +139,12 @@ function renderToNative(rootViewId: number, targetNode: Element): Hippy.NativeNo
warn('Component need to define the native name', targetNode);
return null;
}
if (targetNode.meta.skipAddToDom) {
return null;
}
if (!targetNode.meta.component) {
throw new Error(`Specific tag is not supported yet: ${targetNode.tagName}`);
}
// Translate to native node
const nativeNode: Hippy.NativeNode = {
id: targetNode.nodeId,
@@ -171,15 +168,22 @@ function renderToNative(rootViewId: number, targetNode: Element): Hippy.NativeNo
/**
* Render Element with child to native
* @param rootViewId - rootView id
* @param node - current node
* @param atIndex - current node index
*/
function renderToNativeWithChildren(rootViewId: number, node: ViewNode) {
function renderToNativeWithChildren(rootViewId: number, node: ViewNode, atIndex?: number | undefined) {
const nativeLanguages: Hippy.NativeNode[] = [];
let index = atIndex;
if (typeof index === 'undefined' && node && node.parentNode) {
index = node.parentNode.childNodes.indexOf(node);
}
node.traverseChildren((targetNode: Element) => {
const nativeNode = renderToNative(rootViewId, targetNode);
if (nativeNode) {
nativeLanguages.push(nativeNode);
}
});
}, index);
return nativeLanguages;
}
@@ -194,19 +198,17 @@ function isLayout(node: ViewNode) {
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function insertChild(parentNode: ViewNode, childNode: ViewNode, atIndex = -1) {
if (!parentNode) {
if (!parentNode || !childNode) {
return;
}
if (childNode.meta.skipAddToDom) {
return;
}
const rootViewId = getRootViewId();
// Render the root node
if (isLayout(parentNode) && !parentNode.isMounted) {
// Start real native work.
const translated = renderToNativeWithChildren(rootViewId, childNode);
const translated = renderToNativeWithChildren(rootViewId, childNode, atIndex);
startBatch();
batchNodes.push({
type: NODE_OPERATION_TYPES.createNode,
@@ -217,10 +219,10 @@ function insertChild(parentNode: ViewNode, childNode: ViewNode, atIndex = -1) {
if (!node.isMounted) {
node.isMounted = true;
}
});
// Render others child nodes.
}, atIndex);
// Render others child nodes.
} else if (parentNode.isMounted && !childNode.isMounted) {
const translated = renderToNativeWithChildren(rootViewId, childNode);
const translated = renderToNativeWithChildren(rootViewId, childNode, atIndex);
startBatch();
batchNodes.push({
type: NODE_OPERATION_TYPES.createNode,
@@ -231,20 +233,16 @@ function insertChild(parentNode: ViewNode, childNode: ViewNode, atIndex = -1) {
if (!node.isMounted) {
node.isMounted = true;
}
});
}, atIndex);
}
}
function removeChild(parentNode: ViewNode, childNode: ViewNode) {
function removeChild(parentNode: ViewNode, childNode: ViewNode | null, index: number) {
if (!childNode || childNode.meta.skipAddToDom) {
return;
}
childNode.isMounted = false;
childNode.traverseChildren((targetNode: ViewNode) => {
if (targetNode.isMounted) {
targetNode.isMounted = false;
}
});
childNode.index = index;
const rootViewId = getRootViewId();
const deleteNodeIds: Hippy.NativeNode[] = [{
id: childNode.nodeId,
@@ -71,7 +71,7 @@ function getLinearGradientAngle(value) {
const reg = /^([+-]?\d+\.?\d*)+(deg|turn|rad)|(to\w+)$/g;
const valueList = reg.exec(processedValue);
if (!Array.isArray(valueList)) return;
// default direction is to bottom, i.e. 180degree
// if default direction is to bottom, i.e. 180degree
let angle = '180';
const [direction, angleValue, angleUnit] = valueList;
if (angleValue && angleUnit) { // angle value
@@ -172,28 +172,20 @@ function parseTextShadowOffset(property, value = 0, style) {
class ElementNode extends ViewNode {
constructor(tagName) {
super();
// Tag name
this.tagName = tagName;
// ID attribute in template.
this.id = '';
// style attribute in template.
this.style = {};
// Vue style scope id.
this._styleScopeId = null;
// Class attribute in template.
this.classList = new Set(); // Fake DOMTokenLis
// Other attributes in template.
this.attributes = {};
// Event observer.
this._emitter = null;
// Style pre-processor
this.beforeLoadStyle = getBeforeLoadStyle();
}
@@ -242,7 +234,6 @@ class ElementNode extends ViewNode {
updateChild(this);
return;
}
switch (key) {
case 'class': {
const newClassList = new Set(value.split(' ').filter(x => x.trim()));
@@ -293,7 +284,6 @@ class ElementNode extends ViewNode {
default:
this.attributes[key] = value;
}
updateChild(this);
} catch (err) {
// Throw error in development mode
@@ -312,7 +302,6 @@ class ElementNode extends ViewNode {
delete this.style[property];
return;
}
// Preprocess the style
let {
property: p,
@@ -321,8 +310,7 @@ class ElementNode extends ViewNode {
property,
value,
});
// Process the specifc style value
// Process the specific style value
switch (p) {
case 'fontWeight':
if (typeof v !== 'string') {
@@ -366,7 +354,6 @@ class ElementNode extends ViewNode {
}
}
}
if (v === undefined || v === null || this.style[p] === v) {
return;
}
@@ -400,7 +387,6 @@ class ElementNode extends ViewNode {
appendChild(childNode) {
super.appendChild(childNode);
if (childNode.meta.symbol === Text) {
this.setText(childNode.text);
}
@@ -408,7 +394,6 @@ class ElementNode extends ViewNode {
insertBefore(childNode, referenceNode) {
super.insertBefore(childNode, referenceNode);
if (childNode.meta.symbol === Text) {
this.setText(childNode.text);
}
@@ -416,7 +401,6 @@ class ElementNode extends ViewNode {
moveChild(childNode, referenceNode) {
super.moveChild(childNode, referenceNode);
if (childNode.meta.symbol === Text) {
this.setText(childNode.text);
}
@@ -424,7 +408,6 @@ class ElementNode extends ViewNode {
removeChild(childNode) {
super.removeChild(childNode);
if (childNode.meta.symbol === Text) {
this.setText('');
}
+20 -42
View File
@@ -122,13 +122,17 @@ function endBatch(app) {
}
function getCssMap() {
// To support dynamic import. __cssMap can be loaded from differnet js file.
// __cssMap should be create/append if global[GLOBAL_STYLE_NAME] exists;
/**
* To support dynamic import, __cssMap can be loaded from different js file.
* __cssMap should be create/append if global[GLOBAL_STYLE_NAME] exists;
*/
if (__cssMap && !global[GLOBAL_STYLE_NAME]) {
return __cssMap;
}
// HERE IS A SECRET STARTUP OPTION: beforeStyleLoadHook
// Usage for process the styles while styles loading.
/**
* Here is a secret startup option: beforeStyleLoadHook.
* Usage for process the styles while styles loading.
*/
const cssRules = fromAstNodes(global[GLOBAL_STYLE_NAME]);
if (__cssMap) {
__cssMap.append(cssRules);
@@ -145,7 +149,6 @@ function getCssMap() {
function getNativeProps(node) {
// Initial the props with empty
const props = {};
// Get the default native props from meta
if (node.meta.component.defaultNativeProps) {
Object.keys(node.meta.component.defaultNativeProps).forEach((key) => {
@@ -162,36 +165,31 @@ function getNativeProps(node) {
}
});
}
// Get the proceed props from node attributes
Object.keys(node.attributes).forEach((key) => {
let value = node.getAttribute(key);
// No defined map
if (!node.meta.component.attributeMaps || !node.meta.component.attributeMaps[key]) {
props[key] = value;
return;
}
// Defined mapped props.
const map = node.meta.component.attributeMaps[key];
if (typeof map === 'string') {
props[map] = value;
return;
}
// Define mapped props is a function.
if (isFunction(map)) {
props[key] = map(value);
return;
}
// Defined object map with value
const { name: propsKey, propsValue, jointKey } = map;
if (isFunction(propsValue)) {
value = propsValue(value);
}
// if jointKey set, multi attributes will be assigned to the same jointKey object
// if jointKey set, multi attributes will be assigned to the same jointKey object.
if (jointKey) {
props[jointKey] = props[jointKey] || {};
Object.assign(props[jointKey], {
@@ -274,7 +272,7 @@ function parseViewComponent(targetNode, nativeNode, style) {
/**
* Get target node attributes, use to chrome devTool tag attribute show while debugging
* @param targetNode
* @returns attributes
* @returns attributes|{}
*/
function getTargetNodeAttributes(targetNode) {
try {
@@ -305,13 +303,11 @@ function renderToNative(rootViewId, targetNode) {
if (!targetNode.meta.component) {
throw new Error(`Specific tag is not supported yet: ${targetNode.tagName}`);
}
let style = {};
// Apply styles when the targetNode attach to document at first time.
if (targetNode.meta.component.defaultNativeStyle) {
style = { ...targetNode.meta.component.defaultNativeStyle };
}
// Apply styles from CSS
const matchedSelectors = getCssMap().query(targetNode);
matchedSelectors.selectors.forEach((matchedSelector) => {
@@ -319,10 +315,8 @@ function renderToNative(rootViewId, targetNode) {
style[cssStyle.property] = cssStyle.value;
});
});
// Apply style from style attribute.
style = { ...style, ...targetNode.style };
// Convert to real native event
const events = {};
// FIXME: Bad accessing the private property.
@@ -344,7 +338,6 @@ function renderToNative(rootViewId, targetNode) {
});
}
}
// Translate to native node
const nativeNode = {
id: targetNode.nodeId,
@@ -357,12 +350,11 @@ function renderToNative(rootViewId, targetNode) {
style,
},
};
// Add nativeNode attributes info for debugging
// Add nativeNode attributes info for Element debugging
if (process.env.NODE_ENV !== 'production') {
nativeNode.tagName = targetNode.tagName;
nativeNode.props.attributes = getTargetNodeAttributes(targetNode);
}
parseViewComponent(targetNode, nativeNode, style);
parseTextInputComponent(targetNode, style);
return nativeNode;
@@ -400,18 +392,15 @@ function isLayout(node, rootView) {
}
function insertChild(parentNode, childNode, atIndex = -1) {
if (!parentNode) {
if (!parentNode || !childNode) {
return;
}
if (parentNode.meta && isFunction(parentNode.meta.insertChild)) {
parentNode.meta.insertChild(parentNode, childNode, atIndex);
}
if (childNode.meta.skipAddToDom) {
return;
}
const app = getApp();
if (!app) {
return;
@@ -449,33 +438,22 @@ function insertChild(parentNode, childNode, atIndex = -1) {
}
}
function removeChild(parentNode, childNode) {
function removeChild(parentNode, childNode, index) {
if (parentNode && parentNode.meta && isFunction(parentNode.meta.removeChild)) {
parentNode.meta.removeChild(parentNode, childNode);
}
if (!childNode || childNode.meta.skipAddToDom) {
return;
}
childNode.isMounted = false;
childNode.traverseChildren((node) => {
if (node.isMounted) {
node.isMounted = false;
}
});
childNode.index = index;
const app = getApp();
const deleteNodeIds = [];
childNode.traverseChildren((targetNode) => {
if (targetNode.meta.skipAddToDom) {
return;
}
deleteNodeIds.push({
id: targetNode.nodeId,
index: targetNode.index,
pId: targetNode.parentNode.nodeId,
});
});
const { $options: { rootViewId } } = app;
const deleteNodeIds = [{
id: childNode.nodeId,
pId: childNode.parentNode ? childNode.parentNode.nodeId : rootViewId,
index: childNode.index,
}];
startBatch();
__batchNodes.push({
type: NODE_OPERATION_TYPES.deleteNode,
+13 -47
View File
@@ -95,33 +95,25 @@ class ViewNode {
if (!childNode) {
throw new Error('Can\'t insert child.');
}
if (!referenceNode) {
return this.appendChild(childNode);
}
if (referenceNode.parentNode !== this) {
throw new Error('Can\'t insert child, because the reference node has a different parent.');
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t insert child, because it already has a different parent.');
}
const index = this.childNodes.indexOf(referenceNode);
childNode.parentNode = this;
childNode.nextSibling = referenceNode;
childNode.prevSibling = this.childNodes[index - 1];
// update previous node's nextSibling to prevent patch bug
if (this.childNodes[index - 1]) {
this.childNodes[index - 1].nextSibling = childNode;
}
referenceNode.prevSibling = childNode;
this.childNodes.splice(index, 0, childNode);
return insertChild(this, childNode, index);
}
@@ -129,37 +121,30 @@ class ViewNode {
if (!childNode) {
throw new Error('Can\'t move child.');
}
if (!referenceNode) {
return this.appendChild(childNode);
}
if (referenceNode.parentNode !== this) {
throw new Error('Can\'t move child, because the reference node has a different parent.');
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t move child, because it already has a different parent.');
}
const oldIndex = this.childNodes.indexOf(childNode);
const newIndex = this.childNodes.indexOf(referenceNode);
const referenceIndex = this.childNodes.indexOf(referenceNode);
// return if the moved index is the same as the previous one
if (newIndex === oldIndex) {
if (referenceIndex === oldIndex) {
return childNode;
}
// set new siblings relations
childNode.nextSibling = referenceNode;
childNode.prevSibling = referenceNode.prevSibling;
referenceNode.prevSibling = childNode;
if (this.childNodes[newIndex - 1]) {
this.childNodes[newIndex - 1].nextSibling = childNode;
if (this.childNodes[referenceIndex - 1]) {
this.childNodes[referenceIndex - 1].nextSibling = childNode;
}
if (this.childNodes[newIndex + 1]) {
this.childNodes[newIndex + 1].prevSibling = childNode;
if (this.childNodes[referenceIndex + 1]) {
this.childNodes[referenceIndex + 1].prevSibling = childNode;
}
if (this.childNodes[oldIndex - 1]) {
this.childNodes[oldIndex - 1].nextSibling = this.childNodes[oldIndex + 1];
@@ -167,42 +152,31 @@ class ViewNode {
if (this.childNodes[oldIndex + 1]) {
this.childNodes[oldIndex + 1].prevSibling = this.childNodes[oldIndex - 1];
}
this.childNodes.splice(oldIndex, 1);
// remove old child node from native
removeChild(this, childNode);
// remove old child and insert new child, which is like moving child
removeChild(this, childNode, oldIndex);
const newIndex = this.childNodes.indexOf(referenceNode);
this.childNodes.splice(newIndex, 0, childNode);
this.childNodes.splice(oldIndex + (newIndex < oldIndex ? 1 : 0), 1);
// should filter empty nodes before finding the index of node
const atIndex = this.childNodes.filter(ch => ch.index > -1).indexOf(childNode);
return insertChild(this, childNode, atIndex);
return insertChild(this, childNode, newIndex);
}
appendChild(childNode) {
if (!childNode) {
throw new Error('Can\'t append child.');
}
if (childNode.parentNode && childNode.parentNode !== this) {
throw new Error('Can\'t append child, because it already has a different parent.');
}
// remove childNode if exist
if (childNode.isMounted) {
this.removeChild(childNode);
}
childNode.parentNode = this;
if (this.lastChild) {
childNode.prevSibling = this.lastChild;
this.lastChild.nextSibling = childNode;
}
this.childNodes.push(childNode);
insertChild(this, childNode, this.childNodes.length - 1);
}
@@ -210,36 +184,29 @@ class ViewNode {
if (!childNode) {
throw new Error('Can\'t remove child.');
}
if (!childNode.parentNode) {
throw new Error('Can\'t remove child, because it has no parent.');
}
if (childNode.parentNode !== this) {
throw new Error('Can\'t remove child, because it has a different parent.');
}
if (childNode.meta.skipAddToDom) {
return;
}
removeChild(this, childNode);
// FIXME: parentNode should be null when removeChild, But it breaks add the node again.
// Issue position: https://github.com/vuejs/vue/tree/master/src/core/vdom/patch.js#L250
// childNode.parentNode = null;
if (childNode.prevSibling) {
childNode.prevSibling.nextSibling = childNode.nextSibling;
}
if (childNode.nextSibling) {
childNode.nextSibling.prevSibling = childNode.prevSibling;
}
childNode.prevSibling = null;
childNode.nextSibling = null;
this.childNodes = this.childNodes.filter(node => node !== childNode);
const index = this.childNodes.indexOf(childNode);
this.childNodes.splice(index, 1);
removeChild(this, childNode, index);
}
/**
@@ -275,7 +242,6 @@ class ViewNode {
}
this.index = index;
callback(this);
// Find the children
if (this.childNodes.length) {
this.childNodes.forEach((childNode) => {
+3 -15
View File
@@ -91,7 +91,8 @@ Vue.prototype.$mount = function $mount(el, hydrating) {
/**
* Register the Hippy-Vue app to Native.
*
* @param {function} callback - Callback after register completed.
* @param {function} afterCallback - Callback after register completed.
* @param {function} beforeCallback - Callback before register completed.
*/
Vue.prototype.$start = function $start(afterCallback, beforeCallback) {
setApp(this);
@@ -108,15 +109,13 @@ Vue.prototype.$start = function $start(afterCallback, beforeCallback) {
});
// Register the entry point into Hippy
// The callback will be exectue when Native trigger loadInstance
// The callback will be execute when Native trigger loadInstance
// or runApplication event.
HippyRegister.regist(this.$options.appName, (superProps) => {
const { __instanceId__: rootViewId } = superProps;
this.$options.$superProps = superProps;
this.$options.rootViewId = rootViewId;
trace(...componentName, 'Start', this.$options.appName, 'with rootViewId', rootViewId, superProps);
// Destroy the old instance and set the new one when restart the app
if (this.$el) {
this.$destroy();
@@ -124,15 +123,12 @@ Vue.prototype.$start = function $start(afterCallback, beforeCallback) {
const newApp = new AppConstructor(this.$options);
setApp(newApp);
}
// Call the callback before $mount
if (isFunction(beforeCallback)) {
beforeCallback(this, superProps);
}
// Draw the app.
this.$mount();
// Draw the iPhone status bar background.
// It should execute after $mount, otherwise this.$el will be undefined.
if (Native.Platform === 'ios') {
@@ -145,7 +141,6 @@ Vue.prototype.$start = function $start(afterCallback, beforeCallback) {
}
}
}
// Call the callback after $mount
if (isFunction(afterCallback)) {
afterCallback(this, superProps);
@@ -188,9 +183,7 @@ Vue.extend = function hippyExtend(extendOptions) {
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId];
}
const name = extendOptions.name || Super.options.name;
const Sub = function VueComponent(options) {
this._init(options);
};
@@ -200,7 +193,6 @@ Vue.extend = function hippyExtend(extendOptions) {
Sub.cid = cid;
Sub.options = mergeOptions(Super.options, extendOptions);
Sub.super = Super;
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
@@ -210,12 +202,10 @@ Vue.extend = function hippyExtend(extendOptions) {
if (Sub.options.computed) {
initComputed(Sub);
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend;
Sub.mixin = Super.mixin;
Sub.use = Super.use;
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach((type) => {
@@ -225,14 +215,12 @@ Vue.extend = function hippyExtend(extendOptions) {
if (name) {
Sub.options.components[name] = Sub;
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
// cache constructor
cachedCtors[SuperId] = Sub;
return Sub;