chore: make DOM operations lazily init (#9468)

* chore: make DOM operations lazyily init

* cleanup types

* cleanup types

* cleanup types

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

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>

* single line annotations

* remove unnecessary coercion

* group statements by type

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/9472/head
Dominic Gannaway 1 year ago committed by GitHub
parent 640dd48705
commit 73e8820fe7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: make operations lazy

@ -1,46 +1,109 @@
import { current_hydration_fragment, get_hydration_fragment } from './hydration.js'; import { current_hydration_fragment, get_hydration_fragment } from './hydration.js';
import { get_descriptor } from './utils.js'; import { get_descriptor } from './utils.js';
/** This file is also loaded in server environments, so we need guard against eagerly accessing browser globals */ // We cache the Node and Element prototype methods, so that we can avoid doing
const has_browser_globals = typeof window !== 'undefined'; // expensive prototype chain lookups.
// We cache the Node and Element prototype methods, so that subsequent calls-sites are monomorphic rather /** @type {Node} */
// than megamorphic. var node_prototype;
const node_prototype = /** @type {Node} */ (has_browser_globals ? Node.prototype : {});
const element_prototype = /** @type {Element} */ (has_browser_globals ? Element.prototype : {}); /** @type {Element} */
const text_prototype = /** @type {Text} */ (has_browser_globals ? Text.prototype : {}); var element_prototype;
const map_prototype = Map.prototype;
const append_child_method = node_prototype.appendChild; /** @type {Text} */
const clone_node_method = node_prototype.cloneNode; var text_prototype;
const map_set_method = map_prototype.set;
const map_get_method = map_prototype.get; /** @type {Map<any, any>} */
const map_delete_method = map_prototype.delete; var map_prototype;
// @ts-expect-error improve perf of expando on DOM events
element_prototype.__click = undefined; /** @type {typeof Node.prototype.appendChild} */
// @ts-expect-error improve perf of expando on DOM text updates var append_child_method;
text_prototype.__nodeValue = ' ';
// @ts-expect-error improve perf of expando on DOM className updates /** @type {typeof Node.prototype.cloneNode} */
element_prototype.__className = ''; var clone_node_method;
const first_child_get = /** @type {(this: Node) => ChildNode | null} */ ( /** @type {typeof Map.prototype.set} */
// @ts-ignore var map_set_method;
has_browser_globals ? get_descriptor(node_prototype, 'firstChild').get : null
); /** @type {typeof Map.prototype.get} */
var map_get_method;
const next_sibling_get = /** @type {(this: Node) => ChildNode | null} */ (
// @ts-ignore /** @type {typeof Map.prototype.delete} */
has_browser_globals ? get_descriptor(node_prototype, 'nextSibling').get : null var map_delete_method;
);
/** @type {(this: Node) => ChildNode | null} */
const text_content_set = /** @type {(this: Node, text: string ) => void} */ ( var first_child_get;
// @ts-ignore
has_browser_globals ? get_descriptor(node_prototype, 'textContent').set : null /** @type {(this: Node) => ChildNode | null} */
); var next_sibling_get;
const class_name_set = /** @type {(this: Element, class_name: string) => void} */ ( /** @type {(this: Node, text: string ) => void} */
// @ts-ignore var text_content_set;
has_browser_globals ? get_descriptor(element_prototype, 'className').set : null
); /** @type {(this: Element, class_name: string) => void} */
var class_name_set;
// export these for reference in the compiled code, making global name deduplication unnecessary
/**
* @type {Window}
*/
export var $window;
/**
* @type {Document}
*/
export var $document;
/**
* Initialize these lazily to avoid issues when using the runtime in a server context
* where these globals are not available while avoiding a separate server entry point
*/
export function init_operations() {
if (node_prototype !== undefined) {
return;
}
node_prototype = Node.prototype;
element_prototype = Element.prototype;
text_prototype = Text.prototype;
map_prototype = Map.prototype;
append_child_method = node_prototype.appendChild;
clone_node_method = node_prototype.cloneNode;
map_set_method = map_prototype.set;
map_get_method = map_prototype.get;
map_delete_method = map_prototype.delete;
$window = window;
$document = document;
// the following assignments improve perf of lookups on DOM nodes
// @ts-expect-error
element_prototype.__click = undefined;
// @ts-expect-error
text_prototype.__nodeValue = ' ';
// @ts-expect-error
element_prototype.__className = '';
first_child_get = /** @type {(this: Node) => ChildNode | null} */ (
// @ts-ignore
get_descriptor(node_prototype, 'firstChild').get
);
next_sibling_get = /** @type {(this: Node) => ChildNode | null} */ (
// @ts-ignore
get_descriptor(node_prototype, 'nextSibling').get
);
text_content_set = /** @type {(this: Node, text: string ) => void} */ (
// @ts-ignore
get_descriptor(node_prototype, 'textContent').set
);
class_name_set = /** @type {(this: Element, class_name: string) => void} */ (
// @ts-ignore
get_descriptor(element_prototype, 'className').set
);
}
/** /**
* @template {Element} E * @template {Element} E
@ -191,7 +254,3 @@ function capture_fragment_from_node(node) {
} }
return node; return node;
} }
// export these for reference in the compiled code, making global name deduplication unnecessary
export const $window = has_browser_globals ? window : undefined;
export const $document = has_browser_globals ? document : undefined;

@ -4,6 +4,7 @@ import {
child, child,
clone_node, clone_node,
create_element, create_element,
init_operations,
map_get, map_get,
map_set, map_set,
set_class_name set_class_name
@ -3173,6 +3174,7 @@ export function createRoot(component, options) {
* @returns {[Exports, () => void]} * @returns {[Exports, () => void]}
*/ */
export function mount(component, options) { export function mount(component, options) {
init_operations();
const registered_events = new Set(); const registered_events = new Set();
const container = options.target; const container = options.target;
const block = create_root_block(container, options.intro || false); const block = create_root_block(container, options.intro || false);

Loading…
Cancel
Save