Skip to content

Commit cecbb12

Browse files
committed
Experiment: add moveBefore rather than insertBefor when available
1 parent 46e83a1 commit cecbb12

7 files changed

Lines changed: 40 additions & 5 deletions

File tree

src/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ export const EMPTY_OBJ = /** @type {any} */ ({});
3333
export const EMPTY_ARR = [];
3434

3535
export const MATHML_TOKEN_ELEMENTS = /(mi|mn|mo|ms$|mte|msp)/;
36+
37+
export const HAS_MOVE_BEFORE_SUPPORT =
38+
typeof window !== 'undefined' && 'moveBefore' in Element.prototype;

src/diff/children.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
INSERT_VNODE,
77
MATCHED,
88
UNDEFINED,
9-
NULL
9+
NULL,
10+
HAS_MOVE_BEFORE_SUPPORT
1011
} from '../constants';
1112
import { isArray } from '../util';
1213
import { getDomSibling } from '../component';
@@ -126,8 +127,9 @@ export function diffChildren(
126127
}
127128

128129
let shouldPlace = childVNode._flags & INSERT_VNODE;
130+
const isMounting = oldVNode == NULL || oldVNode._original == NULL;
129131
if (shouldPlace || oldVNode._children === childVNode._children) {
130-
oldDom = insert(childVNode, oldDom, parentDom, shouldPlace);
132+
oldDom = insert(childVNode, oldDom, parentDom, shouldPlace, isMounting);
131133

132134
// When a matched VNode is physically moved via INSERT_VNODE, its old
133135
// _dom pointer becomes a stale positional reference. Clear it so that
@@ -344,9 +346,10 @@ function constructNewChildrenArray(
344346
* @param {PreactElement} oldDom
345347
* @param {PreactElement} parentDom
346348
* @param {number} shouldPlace
349+
* @param {boolean} isMounting
347350
* @returns {PreactElement}
348351
*/
349-
function insert(parentVNode, oldDom, parentDom, shouldPlace) {
352+
function insert(parentVNode, oldDom, parentDom, shouldPlace, isMounting) {
350353
// Note: VNodes in nested suspended trees may be missing _children.
351354
if (typeof parentVNode.type == 'function') {
352355
let children = parentVNode._children;
@@ -357,7 +360,7 @@ function insert(parentVNode, oldDom, parentDom, shouldPlace) {
357360
// children's _parent pointer to point to the newVNode (parentVNode
358361
// here).
359362
children[i]._parent = parentVNode;
360-
oldDom = insert(children[i], oldDom, parentDom, shouldPlace);
363+
oldDom = insert(children[i], oldDom, parentDom, shouldPlace, false);
361364
}
362365
}
363366

@@ -367,7 +370,13 @@ function insert(parentVNode, oldDom, parentDom, shouldPlace) {
367370
if (oldDom && parentVNode.type && !oldDom.parentNode) {
368371
oldDom = getDomSibling(parentVNode);
369372
}
370-
parentDom.insertBefore(parentVNode._dom, oldDom || NULL);
373+
374+
if (HAS_MOVE_BEFORE_SUPPORT && !isMounting) {
375+
// @ts-expect-error This isn't added to TypeScript lib.d.ts yet
376+
parentDom.moveBefore(parentVNode._dom, oldDom);
377+
} else {
378+
parentDom.insertBefore(parentVNode._dom, oldDom || NULL);
379+
}
371380
}
372381
oldDom = parentVNode._dom;
373382
}

test/_util/logCall.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ export function logCall(obj, method) {
3939
}
4040
break;
4141
}
42+
case 'moveBefore': {
43+
if (args[1] === null && args.length === 2) {
44+
operation = `${serialize(this)}.appendChild(${serialize(args[0])})`;
45+
} else {
46+
operation = `${serialize(this)}.insertBefore(${c})`;
47+
}
48+
break;
49+
}
4250
default: {
4351
operation = `${serialize(this)}.${String(method)}(${c})`;
4452
break;

test/browser/fragments.test.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ describe('Fragment', () => {
3838
let resetAppendChild;
3939
let resetRemove;
4040
let resetRemoveText;
41+
let resetMoveBefore;
4142

4243
beforeAll(() => {
4344
resetInsertBefore = logCall(Element.prototype, 'insertBefore');
45+
// @ts-expect-error
46+
resetMoveBefore = logCall(Element.prototype, 'moveBefore');
4447
resetAppendChild = logCall(Element.prototype, 'appendChild');
4548
resetRemove = logCall(Element.prototype, 'remove');
4649
resetRemoveText = logCall(Text.prototype, 'remove');
@@ -57,6 +60,7 @@ describe('Fragment', () => {
5760
});
5861

5962
afterAll(() => {
63+
resetMoveBefore();
6064
resetInsertBefore();
6165
resetAppendChild();
6266
resetRemove();

test/browser/keys.test.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ describe('keys', () => {
7171
values.splice(to, 0, value);
7272
}
7373

74+
let resetMoveBefore;
7475
let resetAppendChild;
7576
let resetInsertBefore;
7677
let resetRemoveChild;
7778
let resetRemove;
7879
let resetRemoveText;
7980

8081
beforeAll(() => {
82+
resetMoveBefore = logCall(Element.prototype, 'moveBefore');
8183
resetAppendChild = logCall(Element.prototype, 'appendChild');
8284
resetInsertBefore = logCall(Element.prototype, 'insertBefore');
8385
resetRemoveChild = logCall(Element.prototype, 'removeChild');
@@ -91,6 +93,7 @@ describe('keys', () => {
9193
resetRemoveChild();
9294
resetRemove();
9395
resetRemoveText();
96+
resetMoveBefore();
9497
});
9598

9699
beforeEach(() => {

test/browser/lifecycles/shouldComponentUpdate.test.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ describe('Lifecycle methods', () => {
1515
// expect(getLog()).to.deep.equal(expectedOperations, message);
1616
// }
1717
let resetInsertBefore;
18+
let resetMoveBefore;
1819
let resetRemoveChild;
1920
let resetRemove;
2021

2122
beforeAll(() => {
23+
// @ts-expect-error
24+
resetMoveBefore = logCall(Element.prototype, 'moveBefore');
2225
resetInsertBefore = logCall(Element.prototype, 'insertBefore');
2326
resetRemoveChild = logCall(Element.prototype, 'appendChild');
2427
resetRemove = logCall(Element.prototype, 'removeChild');
@@ -28,6 +31,7 @@ describe('Lifecycle methods', () => {
2831
resetInsertBefore();
2932
resetRemoveChild();
3033
resetRemove();
34+
resetMoveBefore();
3135
});
3236

3337
beforeEach(() => {

test/browser/render.test.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('render()', () => {
3030
let resetInsertBefore;
3131
let resetRemoveText;
3232
let resetRemove;
33+
let resetMoveBefore;
3334

3435
beforeEach(() => {
3536
scratch = setupScratch();
@@ -43,12 +44,15 @@ describe('render()', () => {
4344
beforeAll(() => {
4445
resetAppendChild = logCall(Element.prototype, 'appendChild');
4546
resetInsertBefore = logCall(Element.prototype, 'insertBefore');
47+
// @ts-expect-error
48+
resetMoveBefore = logCall(Element.prototype, 'moveBefore');
4649
resetRemoveText = logCall(Text.prototype, 'remove');
4750
resetRemove = logCall(Element.prototype, 'remove');
4851
});
4952

5053
afterAll(() => {
5154
resetAppendChild();
55+
resetMoveBefore();
5256
resetInsertBefore();
5357
resetRemoveText();
5458
resetRemove();

0 commit comments

Comments
 (0)