fix: better docs and debuggability

This commit is contained in:
Nolan Lawson 2023-12-16 13:29:44 -08:00
parent ba02269e5a
commit 49cadcfeec
2 changed files with 9 additions and 2 deletions

View File

@ -68,9 +68,9 @@ up with breaking changes in Svelte or the tools in the Svelte ecosystem (e.g. Ro
The framework mostly gets the job done, but I took a few shortcuts since we didn't need all the possible bells and whistles. Here is a brief description.
First, all of the DOM nodes and update functions for those nodes are kept in-memory via a WeakMap pointing at the `state`. There's one `state` per instance of the `Picker.js` Svelte-esque component. So when the instance is GC'ed, everything related to the DOM and update functions should be GC'ed.
First, all the DOM nodes and update functions for those nodes are kept in-memory via a `WeakMap` where the key is the `state`. There's one `state` per instance of the `Picker.js` Svelte-esque component. So when the instance is GC'ed, everything related to the DOM and update functions should be GC'ed. (The exception is the global `parseCache`, which only contains the clone-able `template` and bindings for each unique `tokens` array, which is unique per `html` tag template literal. These templates/bindings never changes per component instance, so it makes sense to just parse once and cache them forever, in case the `<emoji-picker>` element itself is constantly unmounted and re-created.)
Second, I took a shortcut, which is that all DOM nodes and update functions are keyed off of 1) the unique tokens for the tag template literal plus 2) a unique `key` from the `map` function (if it exists). These are only GC'ed when the whole `state` is GC'ed. So in the worst case, every DOM node for every emoji in the picker is kept in memory (e.g. if you click on every tab button), but this seemed like a reasonable tradeoff for simplicity, plus the perf improvement of avoiding re-rendering the same node when it's unchanged (this is especially important if the reactivity system is kind of chatty, and is constantly setting the same arrays over and over the framework just notices that all the `children` are the same objects and doesn't re-render). This also works because the `map`ed DOM nodes are not highly dynamic.
Second, I took a shortcut, which is that all unique (non-`<template>`) DOM nodes and update functions are keyed off of 1) the unique tokens for the tag template literal plus 2) a unique `key` from the `map` function (if it exists). These are only GC'ed when the whole `state` is GC'ed. So in the worst case, every DOM node for every emoji in the picker is kept in memory (e.g. if you click on every tab button), but this seemed like a reasonable tradeoff for simplicity, plus the perf improvement of avoiding re-rendering the same node when it's unchanged (this is especially important if the reactivity system is kind of chatty, and is constantly setting the same arrays over and over the framework just notices that all the `children` are the same objects and doesn't re-render). This also works because the `map`ed DOM nodes are not highly dynamic.
Third, all refs and event listeners are only bound once this just happens to work since most of the event listeners are hoisted (delegated) anyway.

View File

@ -4,6 +4,13 @@ const parseCache = new WeakMap()
const domInstancesCache = new WeakMap()
const unkeyedSymbol = Symbol('un-keyed')
// for debugging
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
window.parseCache = parseCache
window.domInstancesCache = domInstancesCache
}
// Not supported in Safari <=13
const hasReplaceChildren = 'replaceChildren' in Element.prototype
function replaceChildren (parentNode, newChildren) {