Skip to content

Commit 3f42482

Browse files
committed
chore: make DOM operations lazyily init
1 parent 6f3dc04 commit 3f42482

File tree

3 files changed

+113
-44
lines changed

3 files changed

+113
-44
lines changed

.changeset/fresh-weeks-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
chore: make operations lazy

packages/svelte/src/internal/client/operations.js

Lines changed: 106 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,112 @@
11
import { current_hydration_fragment, get_hydration_fragment } from './hydration.js';
22
import { get_descriptor } from './utils.js';
33

4-
/** This file is also loaded in server environments, so we need guard against eagerly accessing browser globals */
5-
const has_browser_globals = typeof window !== 'undefined';
6-
7-
// We cache the Node and Element prototype methods, so that subsequent calls-sites are monomorphic rather
8-
// than megamorphic.
9-
const node_prototype = /** @type {Node} */ (has_browser_globals ? Node.prototype : {});
10-
const element_prototype = /** @type {Element} */ (has_browser_globals ? Element.prototype : {});
11-
const text_prototype = /** @type {Text} */ (has_browser_globals ? Text.prototype : {});
12-
const map_prototype = Map.prototype;
13-
const append_child_method = node_prototype.appendChild;
14-
const clone_node_method = node_prototype.cloneNode;
15-
const map_set_method = map_prototype.set;
16-
const map_get_method = map_prototype.get;
17-
const map_delete_method = map_prototype.delete;
18-
// @ts-expect-error improve perf of expando on DOM events
19-
element_prototype.__click = undefined;
20-
// @ts-expect-error improve perf of expando on DOM text updates
21-
text_prototype.__nodeValue = ' ';
22-
// @ts-expect-error improve perf of expando on DOM className updates
23-
element_prototype.__className = '';
24-
25-
const first_child_get = /** @type {(this: Node) => ChildNode | null} */ (
26-
// @ts-ignore
27-
has_browser_globals ? get_descriptor(node_prototype, 'firstChild').get : null
28-
);
29-
30-
const next_sibling_get = /** @type {(this: Node) => ChildNode | null} */ (
31-
// @ts-ignore
32-
has_browser_globals ? get_descriptor(node_prototype, 'nextSibling').get : null
33-
);
34-
35-
const text_content_set = /** @type {(this: Node, text: string ) => void} */ (
36-
// @ts-ignore
37-
has_browser_globals ? get_descriptor(node_prototype, 'textContent').set : null
38-
);
39-
40-
const class_name_set = /** @type {(this: Element, class_name: string) => void} */ (
41-
// @ts-ignore
42-
has_browser_globals ? get_descriptor(element_prototype, 'className').set : null
43-
);
4+
// We cache the Node and Element prototype methods, so that we can avoid doing
5+
// expensive prototype chain lookups.
6+
7+
/**
8+
* @type {typeof Node.prototype}
9+
*/
10+
var node_prototype;
11+
/**
12+
* @type {typeof Element.prototype}
13+
*/
14+
var element_prototype;
15+
/**
16+
* @type {typeof Text.prototype}
17+
*/
18+
var text_prototype;
19+
var map_prototype;
20+
/**
21+
* @type {typeof Node.prototype.appendChild}
22+
*/
23+
var append_child_method;
24+
/**
25+
* @type {typeof Node.prototype.cloneNode}
26+
*/
27+
var clone_node_method;
28+
/**
29+
* @type {typeof Map.prototype.set}
30+
*/
31+
var map_set_method;
32+
/**
33+
* @type {typeof Map.prototype.get}
34+
*/
35+
var map_get_method;
36+
/**
37+
* @type {typeof Map.prototype.delete}
38+
*/
39+
var map_delete_method;
40+
/**
41+
* @type {(this: Node) => ChildNode | null}
42+
*/
43+
var first_child_get;
44+
/**
45+
* @type {(this: Node) => ChildNode | null}
46+
*/
47+
var next_sibling_get;
48+
/**
49+
* @type {(this: Node, text: string ) => void}
50+
*/
51+
var text_content_set;
52+
53+
/**
54+
* @type {(this: Element, class_name: string) => void}
55+
*/
56+
var class_name_set;
57+
58+
// export these for reference in the compiled code, making global name deduplication unnecessary
59+
/**
60+
* @type {Window}
61+
*/
62+
export var $window;
63+
/**
64+
* @type {Document}
65+
*/
66+
export var $document;
67+
68+
export function init_operations() {
69+
if (node_prototype !== undefined) {
70+
return;
71+
}
72+
node_prototype = /** @type {Node} */ (Node.prototype);
73+
element_prototype = /** @type {Element} */ (Element.prototype);
74+
text_prototype = /** @type {Text} */ (Text.prototype);
75+
map_prototype = Map.prototype;
76+
append_child_method = node_prototype.appendChild;
77+
clone_node_method = node_prototype.cloneNode;
78+
map_set_method = map_prototype.set;
79+
map_get_method = map_prototype.get;
80+
map_delete_method = map_prototype.delete;
81+
// @ts-expect-error improve perf of expando on DOM events
82+
element_prototype.__click = undefined;
83+
// @ts-expect-error improve perf of expando on DOM text updates
84+
text_prototype.__nodeValue = ' ';
85+
// @ts-expect-error improve perf of expando on DOM className updates
86+
element_prototype.__className = '';
87+
$window = window;
88+
$document = document;
89+
90+
first_child_get = /** @type {(this: Node) => ChildNode | null} */ (
91+
// @ts-ignore
92+
get_descriptor(node_prototype, 'firstChild').get
93+
);
94+
95+
next_sibling_get = /** @type {(this: Node) => ChildNode | null} */ (
96+
// @ts-ignore
97+
get_descriptor(node_prototype, 'nextSibling').get
98+
);
99+
100+
text_content_set = /** @type {(this: Node, text: string ) => void} */ (
101+
// @ts-ignore
102+
get_descriptor(node_prototype, 'textContent').set
103+
);
104+
105+
class_name_set = /** @type {(this: Element, class_name: string) => void} */ (
106+
// @ts-ignore
107+
get_descriptor(element_prototype, 'className').set
108+
);
109+
}
44110

45111
/**
46112
* @template {Element} E
@@ -191,7 +257,3 @@ function capture_fragment_from_node(node) {
191257
}
192258
return node;
193259
}
194-
195-
// export these for reference in the compiled code, making global name deduplication unnecessary
196-
export const $window = has_browser_globals ? window : undefined;
197-
export const $document = has_browser_globals ? document : undefined;

packages/svelte/src/internal/client/render.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
child,
55
clone_node,
66
create_element,
7+
init_operations,
78
map_get,
89
map_set,
910
set_class_name
@@ -3173,6 +3174,7 @@ export function createRoot(component, options) {
31733174
* @returns {[Exports, () => void]}
31743175
*/
31753176
export function mount(component, options) {
3177+
init_operations();
31763178
const registered_events = new Set();
31773179
const container = options.target;
31783180
const block = create_root_block(container, options.intro || false);

0 commit comments

Comments
 (0)