Вот мое наивное понимание того, как работает DOM и браузер

Всякий раз, когда что-то в DOM (реальном dom) изменяется, браузер перерисовывает или повторно отображает этот DOM. Таким образом, проще говоря, каждый раз, когда DOM меняет браузер, необходимо пересчитать CSS, сделать макет и перерисовать веб-страницу. Это то, что требует времени в реальной жизни.

Итак, React поставляется с этим виртуальным DOM, и на самом деле он объединяет изменения, и call применяет их на real-dom за один раз. Таким образом, минимизируется повторный поток и повторная окраска.

Тогда как насчет Svelte. Если он напрямую манипулирует DOM, как он управляет перерисовкой / перекомпоновкой браузера.

Обе библиотеки минимизируют количество изменений, которые необходимо внести в DOM. Разница в том, как они выясняют, что это за минимальный набор изменений.

Подход React заключается в представлении dom в памяти (виртуальный dom). Когда вы устанавливаете состояние, он запускает процесс рендеринга снова, чтобы создать еще один виртуальный домен. Он сравнивает «до» и «после», находит то, что изменилось, и затем любые изменения переносятся в реальный дом.

Подход Svelte заключается в том, что когда вы устанавливаете переменную, он устанавливает флаг, помечающий эту переменную как измененную. Он знает, какие переменные зависят от других переменных, поэтому он просматривает любые зависимые переменные и пересчитывает их, формируя список того, что необходимо изменить. Затем эти изменения переносятся в дом.

В дополнение к (правильному) ответу выше: Svelte «компилирует» код, который вы ему предоставляете, так что окончательный код может быть выполнен без времени выполнения библиотеки (в отличие от React). И он создает достаточно читаемый код, так что абсолютно возможно понять внутреннюю работу.

Примечание: это будет более длинный ответ — и все же опустить много мелких деталей о том, что происходит под капотом Svelte. Но я надеюсь, что это помогает демистифицировать некоторые аспекты того, что происходит под капотом. Кроме того, именно так Svelte работает с v3.16.x. Поскольку это внутреннее, это может измениться. Тем не менее, я все еще нахожу всегда полезным понять, что на самом деле происходит.

Итак, поехали.

Прежде всего: в учебнике Svelte есть полезная функция, позволяющая увидеть сгенерированный код (прямо рядом с панелью «Результат»). Поначалу это может показаться немного пугающим, но вы быстро освоитесь.

Ниже приведен код, основанный на этом примере (но дополнительно упрощенный): руководство по Svelte — реактивность / задания

Наш пример определения компонента (т.е. App.svelte ) выглядит следующим образом:

{amp}lt;script{amp}gt; let count = 0; function handleClick() { count  = 1; } {amp}lt;/script{amp}gt; {amp}lt;button on:click={handleClick}{amp}gt;{count}{amp}lt;/button{amp}gt; 

На основе этого определения компонента компилятор Svelte создает функцию, которая создает «фрагмент», который получает и взаимодействует с «контекстом».

 function create_fragment(ctx) { let button; let t; let dispose; return { c() { button = element("button"); t = text(/*count*/ ctx[0]); dispose = listen(button, "click", /*handleClick*/ ctx[1]); }, m(target, anchor) { insert(target, button, anchor); append(button, t); }, p(ctx, [dirty]) { if (dirty {amp}amp; /*count*/ 1) set_data(t, /*count*/ ctx[0]); }, i: noop, o: noop, d(detaching) { if (detaching) detach(button); dispose(); } }; } 

Фрагмент отвечает за взаимодействие с DOM и будет передаваться вместе с экземпляром компонента. В двух словах, код внутри

  • «c» будет выполняться при создании (создание элементов DOM в памяти, а также настройка обработчиков событий)
  • «m» будет работать на креплении (прикрепление элементов к DOM)
  • «р» будет запускаться при обновлении , т.е. когда что-то (в том числе реквизит) меняется
  • «i» / «o» относятся к интро / аутро (то есть переходам)
  • «д» будет работать на уничтожение

Примечание. Такие функции, как element или set_data , на самом деле очень доступны. Например, элемент функции является просто оболочкой для document.createElement :

 function element(name) { return document.createElement(name); } 

Контекст (ctx) будет содержать все переменные экземпляра, а также функции. Это не более чем простой массив. Поскольку Svelte «знает», что означает каждый индекс во время компиляции, он может делать жесткие ссылки на индексы в других местах.

Этот код по существу определяет контекст экземпляра:

 function instance($$self, $$props, $$invalidate) { let count = 0; function handleClick() { $$invalidate(0, count  = 1); } return [count, handleClick]; } 

Как метод экземпляра, так и create_fragment будут вызываться из другого вызова функции init . Это немного сложнее, так что вместо того, чтобы копировать и вставлять его здесь, вы можете взглянуть на эту ссылку на источник .

$$ invalidate проверит , что переменная count установлена ​​как грязная, и запланирует обновление. Когда будет запущено следующее обновление, оно проверит все «грязные» компоненты и обновит их. То, как это происходит, на самом деле больше деталей реализации. Если интересно, установите точку останова в функции сброса .

На самом деле, если вы действительно хотите пойти немного дальше, я рекомендую клонировать шаблонное приложение, затем создать простой компонент, скомпилировать его и затем проверить «bundle.js». Вы также можете отладить реальный код, если вы либо удалите исходные карты, либо деактивируете их.

Так, например, установите rollup.config.js так:

  output: { sourcemap: false, format: 'iife', name: 'app', file: 'public/build/bundle.js' }, plugins: [ svelte({ dev: false, 

Примечание. Как показано выше, я также рекомендую установить для режима dev значение false, поскольку это создаст более сжатый код.

Одна полезная особенность: когда наше приложение запущено, вы также можете получить доступ к переменной приложения (она назначается глобальному объекту окна, так как она связана как выражение для немедленного вызова функции).

Итак, вы можете открыть консоль и просто сказать

 console.dir(app) 

который будет производить что-то вроде этого

 App $$: fragment: {c: ƒ, m: ƒ, p: ƒ, i: ƒ, o: ƒ, …} ctx: (2) [0, ƒ] props: {count: 0} update: ƒ noop() not_equal: ƒ safe_not_equal(a, b) bound: {} on_mount: [] on_destroy: [] before_update: [] after_update: [] context: Map(0) {} callbacks: {} dirty: [-1] __proto__: Object $set: $$props ={amp}gt; {…} 

Одна интересная особенность заключается в том, что вы можете самостоятельно использовать метод $ set для обновления экземпляра. Например вот так:

 app.$set({count: 10}) 

Есть также Svelte DevTools, которые пытаются сделать внутреннее пространство Svelte более доступным. Почему-то они влияли на производительность рендеринга моих приложений, когда я лично их пробовал, поэтому я сам ими не пользуюсь. Но конечно на что посмотреть.

Ну, вот и все. Я знаю, что это все еще довольно технически, но я надеюсь, что это помогло лучше понять, что делает скомпилированный код Svelte.