Легенда о Фреймворке Всевластия

    В последнее время набирает популярность тренд «исчезающих фреймворков», локомотивом которого, без сомнения, можно считать SvelteJS — buildtime-фреймворк и компилятор в ванильный javascript.

    Несмотря на то, что концептуально Svelte весьма прост, а в использовании еще проще, многие разработчики задаются вопросом, в чем же killer-фича данного фреймворка, да и подхода в целом? Почему это не «yet another javascript framework»?

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

    Давайте разберемся, но сначала я расскажу вам одну легенду…



    Легенда о Фреймворке Всевластия


    Одни фреймворки были созданы ребятами из Google и Facebook, другие — крутыми чуваками, но все под под пристальным «вниманием» Рича Харриса.

    Девять фреймворков были созданы для людей, семь, по всей видимости, для гномов. Ещё три фреймворка (react, vue, angular) были для эльфов.

    После создания фреймворков и их имплементации в тысячи проектов, Рич Харрис самолично и тайно создал один фреймворк…

    One framework to rule them all,
    One framework to find them,
    One framework to bring them all
    And together bind them.

    — The Lord of the Frameworks

    Проблема


    Уверен, многие из вас, кто всерьез и долго занимается фронтенд-разработкой, не раз сталкивались с проблемой выбора инструментов для вашего текущего и/или следующего проекта.
    Многообразие всевозможных пакетов, утилит, китов, фреймворков, библиотек и других решений, зашкаливает как никогда ранее. А главное вся эта движуха продолжает ускоряться.

    Все это, так или иначе, касается и выбора фреймворка. Наверное не ошибусь, если предположу, что лишь немногие современные команды и компании, начинают новые проекты без использования какого-либо js фреймворка. Конечно если речь идет о современных веб-приложениях, а не просто о веб-сайтах. И все бы ничего, если бы от этого не зависело так многое в вашем проекте.


    Посудите сами, от выбранного вами фреймворка, во многом, зависит состав и характеристики команда, и уж точно сильно зависит весь процесс хантинга. Иногда от этого даже зависят бюджет и сроки. Короче бррр.

    Но реальные проблемы начинаются, если где-нибудь в середине проекта, вы понимаете, что сделали не совсем верный выбор. Что-то не срослось и не закрутилось. Фреймворк потребовал немного больше времени на освоение, немного большей команды, оказался немного менее быстрым, немного не подошел вашим целям или стилю разработки и т.д. А самое главное теперь ваш проект на 100% завязан на этом фреймворке и вы не можете просто взять и переписать его на чем-то другом.

    Еще более обидно, когда все же закончив проект успехом, вы понимаете, что в целом не очень то и довольны. И наверное, не хотели бы писать следующий проект на этом же фреймворке. А значит, все те «переиспользуемые» решения, к которым мы с вами стремимся, можно выкинуть в трубу.

    Реально, черт с ним с бизнес-кодом, реализующим конкретную бизнес-задачу, работает и ладно. Но ведь вы написали такой классный "%вставить свое% с блекджеком и девушками с пониженной социальной ответственностью", и так хотели использовать его в следующем своем проекте, но эта зараза тесно завязана на текущем фреймворке от одного вида которого вас уже воротит.

    Другая вариация этой же проблемы — представьте что вы крупная компания, по типу Яндекса. У вас хуилион проектов, даже таких о которых знают лишь некоторые сотрудники, и каждый проект уже испытал на себе все то, что я описал выше. Проблема в том, что все эти проекты сидят и ненавидят разные фреймворки, которые они выбрали изначально.

    А тут еще ваше замечательное руководство, решило составить конкуренцию Google Material Design и отправить вас в крестовый поход на разношерстные интерфейсы ваших проектов дабы привести их к общему знаменателю. Ушлые дизайнеры уже рисуют новые кнопки и селекторы и строчат тысячи страниц гайдлайнов для нового единого UI-kit'а ваших компонентов. Ура товарищи!

    Не жизнь, а сказка, правда? Осталось только придумать как бы так натянуть все эти новые компоненты на все те проекты, которые вы уже успели понаписать на всех возможных фреймворках. Если времени и денег реально много и есть эстетическое желание, а главное вера, в то что «все надо унифицировать», то можно посадить пару тройку десятков команд переписать все это снова, например на React. Это и правильно, потому что то унылое говно, на котором вы писали последние 2-3 года, уже морально устарело, а вот React будет вечен. Ну-ну)

    Есть и другой путь. Можно написать чудесный новый UI-kit на одном фреймворке, создать как бы библиотеку переиспользуемых компонентов, а потом просто использовать этот UI-kit во всех своих проектах. Прикольно звучит? Конечно, но остается одна проблема — рантайм.

    Если у вас проект написан на Angular (~500Kb), а UI-kit вы решили писать на React (~98Kb), то тащить в каждый проект на одном фреймворке, другой фреймворк, да еще с кучей зависимостей и сам UI-kit, прям скажем не выглядит оптимальным.

    Решение


    На помощью нам приходят те самые «исчезающие» фреймворки, без рантайма. Главное условие тут, чтобы они были максимально изолированные в плане своей экосистемы и имели механизмы внешней интеграции и соответствующие API.

    Прекрасный пример такого фреймворка — SvelteJS, про который уже написано не мало статьей на Хабре.

    Итак, представим ситуацию, что у нас есть приложение на React. Возможно он нам надоел, и мы хотим от него избавиться, но переписать все разом непозволительная роскошь. А может быть, некоторые части приложения требуют улучшения или рефакторинга. Ну или мы решили делать единую библиотеку компонентов, и поэтому теперь все компоненты будем писать на Svelte и использовать во всех проектах. Представили? Да конечно нет, ни у кого нет такой фантазии. Давайте лучше разберем на реальном примере.


    Disclaimer
    Сразу хочу обратить ваше внимание, что я не являюсь React-разработчиком и последний раз «щупал» React в далеком 2015 году. Поэтому, предполагаю, что то, как я написал часть примера на React может задеть чувства верующих реактоводов. Прощу не судить строго, тем более что смысл статьи от этого не меняется.


    Итак, задача внедрить в React приложение уже готовый Svelte-компонент, без изменения самого компонента и без накручивания дополнительного рантайма в приложение. Для примера, возьму компонент осуществляющий поиск по пользователям GitHub, который я писал для предыдущей статьи «Как сделать поиск пользователей по GitHub без React + RxJS 6 + Recompose».

    Код этого компонента можно посмотреть в REPL, а код примера из данной статьи в репозиторий.

    Create React App


    Для начала создадим новый React проект, воспользовавшись де-факто стандартной тулзой — create-react-app:

    npx create-react-app my-app
    cd my-app
    npm start
    

    Ок, если зайти на 3000-й порт, вроде бы работает.

    Настраиваем Svelte


    Если вы ничего не знаете о Svelte, то скажу так, в контексте задачи Svelte — это всего лишь еще один шаг вашего сборщика (webpack/rollup/gupl/grunt/etc), который позволит вам писать компоненты в формате SFC и компилировать их в vanilla javascript.

    В сообществе Svelte больше предпочитают Rollup, что не мудрено, так как у них один автор — Рич Харрис. Однако, так как CRA использует webpack, то мы настроим Svelte через него. Для этого, сначала надо вынести конфиги webpack из react-scripts в проект, чтобы мы могли менять их. Делается это с помощью встроенной команды:

    npm run eject


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

    Теперь, когда конфиги webpack в корне проекта, можно ставить Svelte:

    npm i --save-dev svelte svelte-loader
    


    Обратите внимание на флажок --save-dev, помните да, что рантайма-то нету.))))

    Последний штрих, нужно подключить соответствующий лоадер в конфиги:

      {
        test: /\.svelte$/,
          use: {
            loader: 'svelte-loader',
          }
       },
    


    Вообще, в сообществе Svelte принято писать файлы компонентов с расширением .html, потому что компонент Svelte — это валидный HTML файл. Однако, чтобы избежать вероятных коллизий, в некоторых случаях, лучше использовать кастомный формат файла .svelte.

    Так мы и сделали, теперь все файлы .svelte подключаемые в проект будут перехватываться этим лоадером и компилироваться Svelte.

    Пишем компонент Svelte


    Сперва лучше настроить редактор кода, например, чтобы он применял подсветку html-синтаксиса к файлам с соответствующим расширением. Примерно так это делается в VS Code:

      "files.associations": {
        "*.svelte": "html"
      }
    

    Теперь создадим папочку ./src/svelte_components/ и там папку самого компонента. После просто переносим все файлы из REPL примера в эту папку, попутно давая им новое расширение .svelte, а файл App.html назовем Widget.svelte.

    В итоге должно получиться, что-то вроде этого:


    Там же создаем файл index.js, в котором у нас будет располагаться код интеграции Svelte и React.

    Интегрируем


    Наверное, теперь вы хотите узнать в чем же магия? Магия в том, что всю магию мы уже с вами сделали. Магически, не правда ли?

    Серьезно, теперь мы можем использовать компоненты Svelte в нашем React приложении как совершенно обычные JS конструкторы, а значит код интеграции со Svelte ничем не будет отличаться от интеграции с любой другой standalone либой. Документация React даже содержит раздел посвященный этому: Integrating with Other Libraries .

    Код интеграции может выглядеть например так:

    import React, { PureComponent } from 'react';
    
    import Widget from './Widget.svelte';
    
    export default class extends PureComponent {
    
      componentDidMount() {
        
        const { username } = this.props;
        
        this.widget = new Widget({
          target: this.el,
          data: { username }
        });
    
      }
      
      componentWillUnmount() {
        this.widget.destroy();
      }
      
      render() {
        return (
          <div ref={el => this.el = el}></div>
        );
      }
    }
    

    Итого, мы просто обернули код нашего сложного Svelte компонента в очень простой компонент React, который просто создает новый экземпляр Svelte компонента, передавая при его создании элемент для монтирования и данные из пропсов. Кроме того, мы не забываем демонтировать Svelte компонент в хуке componentWillUnmount.

    Единственное, что мы пока не сделали — не синхронизировали значения стейта компонентов. То есть, если вышестоящий компонент прокинул другие пропсы в компонент-обертку, то они должны примениться к Svelte компоненту. И наоборот, если данные были изменены внутри Svelte компонента, они должны быть прокинуты обратно.

    Для этого, будем считать, что вышестоящий React компонент, будет передавать коллбек onChange, которую мы должны дергать, когда внутри происходят изменения, а изменения пропсов компонента-обертки будем ожидать в хуке componentWillReceiveProps. Давайте сделаем это:

      componentDidMount() {   
        ...
        this.widget.on('state', ({ current: { username }, changed }) => {
            if (changed.username) {
              this.props.onChange({ username });
            } 
        });
      }
    
      componentWillReceiveProps({ username }) {
        this.widget.set({ username });
      }
    

    Здесь мы использовали встроенное событие state, которое срабатывает каждый раз, когда стейт Svelte компонента меняется. В коллбек передается объект содержащий текущий стейт компонента (current), предыдущий стейт (previous) и список измененных свойcтв (changed). Соответственно, мы просто проверяем был ли изменен username и вызываем коллбек onChange, если это так.

    В хуке componentWillReceiveProps мы устанавливаем новое значение username с помощью встроенного метода set().

    Кроме встроенных, компоненты Svelte могут имплементировать кастомные события и методы. Именно эти приятные возможности позволяют описать интерфейс компонента и довольно удобно организовать коммуникацию с «внешним миром».

    Используем


    Теперь попробуем использовать наш виджет непосредственно в React приложении. Для этого отредактируем App.js файл, сгенерированный стартером:

    import React, { Component } from 'react';
    import './App.css';
    
    import GithubWidget from './svelte_components/GithubWidget';
    
    class App extends Component {
    
      constructor() {
        super();
        this.state = {
          username: ''
        };
      }
    
      handleChange = (state) => {
        this.setState({ ...state });   
      }
    
      render() {
        return (
          <div className="App">
            <header className="App-header">
              <h1>Github Widget for: {this.state.username}</h1>
              <GithubWidget
                onChange={this.handleChange} 
                username={this.state.username}
              />
            </header>
          </div>
        );
      }
    }
    
    export default App;
    

    Короче используем как обычный React компонент. И в результате получаем:


    Уже не плохо, правда?) Обратите внимание, значение username, которое мы вводим в текстовое поле виджета сразу же пробрасывается наверх в React приложение.

    Доработаем


    Давайте теперь научим наш виджет искать и выводить не только карточку пользователя GitHub, но и карточку репозитория.

    Во-первых, нужно создать новый компонент Repo.svelte, который как раз и будет отрисовывать карточку репозитория. Для простоты примера, я просто скопипастил шаблон и стили из User.svelte и адаптировал под данные репозитория. Однако, теоретически это отдельный компонент.

    Далее, нужно научить управляющий компонент Widget.svelte переключать эти два вида карточек на лету. Кроме того, нужно научить его дергать разные запросы для пользователя и репозитория.

    Мы будем использовать одно поле для ввода и определять тип данных по наличию "/" в значении. То есть, если нужно поискать пользователя, вводим его username, а если репозиторий, тогда вводим username пользователя, далее "/" и имя репозитория.

    На первый взгляд выглядит довольно заморочено, но на Svelte решение займет буквально 5-6 строк кода. Для начала подключим новый компонент и метод API, который обернем в debounce:

    ...
    import Repo from './Repo.svelte';
    ...
    import { getUserCard, getRepoCard } from './api.js';
    ...
    const getRepo = debounce(getRepoCard, 1000);
    

    Далее, создадим вычисляемое свойство, которое будет определять какой тип карточки мы должны показывать:

    computed: {
      ...
      repo: ({ username }) => username.includes('/'),
      ...
    }
    

    Теперь добавим переключалку запросов к API:

    computed: {
      ...
      card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username),
      ...
    }
    

    И напоследок, переключалка компонентов карточки в зависимости от типа:

    computed: {
      ...
      Card: ({ repo }) => repo ? Repo : User,
      ...
    }
    

    Кроме того, чтобы динамически подменять компоненты, нам необходимо использовать специальный тэг Svelte, который отрисовывает тот компонент, значение которого передано в аттрибут this:

    <svelte:component this={Card} {...card} />
    


    Работает. Обратили внимание? Мы уже пишем на Svelte внутри React приложения! )))

    Теперь давайте, научим наш виджет скрывать поле для ввода и попробуем вводить username не внутри виджета, а внутри React приложения. Мало ли откуда наша бизнес-логика будет получать это значение.

    Введем новое свойство search, значение по-умолчания которого будет false. В зависимости от этого свойства, будет отображаться или не отображаться поле для ввода. По-умолчанию, соответственно, поля не будет.

    {#if search}
    <input bind:value=username placeholder="username or username/repo">
    {/if}
    ...
    <script>
      export default {
        ...
        data() {
          return {
            username: '',
    	search: false
          };
        },
        ...
      };
    </script>
    

    Теперь в App.js создадим поле для ввода на стороне React приложения и напишем соответствующую обработку события ввода:

      ...
      handleUsername = (e) => {
        this.setState({ username: e.target.value });   
      }
      ...
              <h1>Github Widget for: {this.state.username}</h1>
              <input 
                value={this.state.username}
                onChange={this.handleUsername}
                className="Username" 
                placeholder="username or username/repo" 
              />
    

    А еще копипастим в папку с виджетом вот такой вот svg спиннер на Svelte:

    <svg
      height={size}
      width={size}
      style="animation-duration:{speed}ms;"
      class="svelte-spinner"
      viewbox="0 0 32 32"
    >
      <circle
        role="presentation"
        cx="16"
        cy="16"
        r={radius}
        stroke={color}
        fill="none"
        stroke-width={thickness}
        stroke-dasharray="{dash},100"
        stroke-linecap="round"
      />
    </svg>
    
    <script>
      export default {
        data() {
          return {
            size: 25,
            speed: 750,
            color: 'rgba(0,0,0,0.4)',
            thickness: 2,
            gap: 40,
            radius: 10
          };
        },
        computed: {
          dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100
        }
      };
    </script>
    
    <style>
        .svelte-spinner {
            transition-property: transform;
            animation-name: svelte-spinner_infinite-spin;
            animation-iteration-count: infinite;
            animation-timing-function: linear;
        }
        @keyframes svelte-spinner_infinite-spin {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }
    </style>
    

    И применим его в виджете, чтобы было совсем красиво:

    ...
    {#await card}
      <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" />
    {:then card}
    ...
    

    По-моему, получилось очень даже не плохо:


    Верхняя шапка с черным фоном и полем для ввода — это React приложение, белый блок снизу — Svelte виджет. Вот такие пироги. )))

    Репозиторий

    Выводы


    Svelte — отличный инструмент для разработки современных веб-приложений, основанных на компонентном подходе. Кроме того, на нем можно быстро и удобно писать переиспользуемые standalone UI компоненты и виджеты, которые могут быть использованы в любых веб-приложениях, даже совместно с другими фреймворками. Еще он прекрасно подходит для микрофронтендов.

    Svelte прекрасно подойдет вам если:


    1. Вы хотите начать новый проект и не знаете какой фреймворк выбрать для этого.
    2. У вас есть проект, он работает и его лучше не трогать. Новые компоненты и модули, вы можете писать на Svelte и бесшовно интегрировать в существующий код.
    3. У вас уже есть проект, но он устарел частично или полностью и/или требует серьезного рефакторинга, вплоть до полного переписывания. Вы можете начать переписывать его по частям. При этом вам не нужно придумывать сложные конфигурации. Вы просто берете какой-то компонент, переписываете его на Svelte и оборачиваете новый компонент посредством старого. При этом остальные части приложения даже не догадываются об изменениях.
    4. У вас несколько проектов на разных кодовых базах и при этом, вам бы хотелось иметь единый UI-kit и использовать его в любом из этих проектов. Пишите UI-kit на Svelte и используйте его где угодно. Это приятно.

    Хотите узнать больше интересных кейсов? Присоединяйтесь к нашему Telegram-каналу!

    UPDATE: спасибо justboris за правильный вопрос. Продолжая пример:

    import React, { PureComponent } from 'react';
    
    import Widget from './Widget.svelte';
    
    export default class extends PureComponent {
    
      componentDidMount() {
        ...    
    
        this.widget = new Widget({
          target: this.el,
          data: { username },
          slots: {
            default: this.slot
          }
        });
    
        ...
      }
      ...
      render() {
        return (
          <div ref={el => this.el = el}>
            <div ref={el => this.slot = el}>
              {this.props.children}
            </div>
          </div>
        );
      }
    }
    


    <GithubWidget onChange={this.handleChange}  username={this.state.username}>
      <p>Hello world</p>
    </GithubWidget>
    
    Поделиться публикацией
    Комментарии 12
      +2

      Небольшое упрощение: можно называть файлы ".svelte.html". Тогда webpack loader получится сматчить на /\.svelte\.html$/, а никаких настроек для поддержки в редакторах будет не нужно.


      Не все в команде пользуются одинаковым редактором (VSCode, например), так что поддержка нормального воркфлоу для всех может оказаться затруднительной.

        0
        Да, в чем-то вы правы. Однако подобный вариант не убережет от коллизий с тем же CRA, к сожалению, потому что там уже есть конфиги для .html.

        Вообще, когда речь идет о SFC часто используют кастомное расширение, тот же Vue использует .vue. И как мне кажется добавить стандартную подсветку к нестандартному расширению файла можно в любом редакторе.
        +1

        А вот единую UI-библиотеку для React написать не на React не получится. Проблема вот с такими компонентами:


        <Card>
          <h2>Title</h2>
          <div>Some content</div>
        </Card>

        Нужно отрендерить svelte-компонент, а внутрь него вставить React. При этом в React-разметке могут снова встретиться svelte-компоненты (Button какой-нибудь). Такой зебра-рендеринг (react-svelte-react-svelte) смотрится странно и производительность вызывает вопросы.


        Подозреваю, что и с Angular возникнут подобные проблемы.


        А на одних терминальных компонентах без html-контента полноценную UI-библиотеку не выстроишь.

          0
          Да не, такое тоже можно сделать. Svelte поддерживает слоты и именованные слоты. Можно сделать так:

          this.widget = new Widget({
          import React, { PureComponent } from 'react';
          
          import Widget from './Widget.svelte';
          
          export default class extends PureComponent {
          
            componentDidMount() {
              ...    
          
              this.widget = new Widget({
                target: this.el,
                data: { username },
                slots: {
                  default: this.slot
                }
              });
          
              ...
            }
            ...
            render() {
              return (
                <div ref={el => this.el = el}>
                  <div ref={el => this.slot = el}>
                    {this.props.children}
                  </div>
                </div>
              );
            }
          }
          


          И использовать:

          <GithubWidget onChange={this.handleChange}  username={this.state.username}>
            <p>Hello world</p>
          </GithubWidget>
          


          В результате получите:


          Hello world заинжекчен из реакта в svelte компонент.

          Такой зебра-рендеринг (react-svelte-react-svelte) смотрится странно и производительность вызывает вопросы.

          Так никакого зебра-рендеринга же нет. В итоге компоненты Svelte оборачиваются в чистые компоненты React и за их пределами никто даже не догадывается он его существовании. Поэтому вся зебра будет чисто реактовская.

          В целом ваши опасения понятны и, наверное, лучше чтобы компоненты Svelte были максимально изолированы, но все же сделать такое можно.

            0

            Попробовал локально, работает прекрасно, но что делать с динамическим контентом?


            Что-то типа такого в реакте:


            function MenuList({ title, items, renderItem }) {
              return (
                <div>
                  <h2>{title}</h2>
                  {items.map(item => renderItem(item))}
                </div>
              );
            }

            используется так


            <MenuList
              title="TODO"
              items={items}
              renderItem={item => <div>{item.title}</div>}
            />;

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


            Не подумайте, что я докапываюсь, идея интересная, просто разработка UI-либы на svelte — авантюра серьезная, было бы неловко узнать спустя полгода разработки, что нельзя реализовать запрошенный компонент из-за ограничений платформы.

              0
              Как слоты могут быть статическими? Svelte не делает и не может делать никаких предположений о том, что находится внутри слотов. Поэтому он никак не влияет на них. Там может быть что угодно, что текущий компонент, реализующий слот, может не уметь резолвить. И уж точно они не могут быть статическими.

              Продолжая тот же пример:

              Widget.svelte
              <slot name="todo"></slot>
              


              index.js
              ...
                  this.widget = new Widget({
                    target: this.el,
                    data: { username },
                    slots: {
                      default: this.slot,
                      todo: this.todo
                    }
                  });
              ...
              
                render() {
                  return (
                    <div ref={el => this.el = el}>
                      <div ref={el => this.slot = el}>
                        {this.props.children}
                      </div>
                      <div ref={el => this.todo = el} className="todo">
                        <h2>{this.props.title}</h2>
                        {this.props.items.map(item => this.props.renderItem(item))}
                      </div>
                    </div>
                  );
                }
              ...
              


              App.js
              ...
                  this.state = {
                    username: '',
                    items: [
                      {id: 1, title: 'First'},
                      {id: 2, title: 'Second'},
                      {id: 3, title: 'Third'},
                    ]
                  };
              ...
                addToDo = () => {
                  const id = this.state.items.length + 1;
                  this.state.items.push({id, title: `Added ${id}`})
                  this.setState({
                    items: this.state.items
                  })
                }
              ...
                        <button onClick={this.addToDo}>Add todo</button>
                        <GithubWidget
                          onChange={this.handleChange} 
                          username={this.state.username}
                          items={this.state.items}
                          renderItem={item => <div key={item.id}>{item.title}</div>}
                        >
                          <p>Hello world</p>
                        </GithubWidget>
              ...
              


              Вот так примерно работает — сверху React, снизу Svelte:



              Этот и предыдущий пример со слотами закомитил бранч slots
                0

                Вот этот фрагмент разметки


                <div ref={el => this.todo = el} className="todo">
                   <h2>{this.props.title}</h2>
                   {this.props.items.map(item => this.props.renderItem(item))}
                </div>

                Это же уже обычный React, а не Svelte. К другим фреймворкам придется дублировать его еще раз.


                В идеале, хотелось бы такой svelte-компонент


                <h2>{title}</h2>
                <ul>
                    {#each items as item}
                          <li>
                            <slot name="item{item.id}">
                          </li>
                    {:else}
                        <li><i>Empty list</i></li>
                    {/each}
                </ul>

                Но сборщик справедливо ругается, что slot не может быть использован в цикле, поэтому такой номер не прокатывает.

                  0
                  А, теперь понял ваш пример. Только вопрос какой смысл в таком использовании слотов, если вы ничего не можете сделать с item? То есть нельзя ничего передать из Svelte в слот. Правило простое — где идет работа с массивом, там идет работа из его элементом.

                  Если вам нужно уметь выводить в списке айтемы в виде разных компонентов, то это легко реализуется на Svelte. А управляться может из React.

                  Например делаем абстрактный ListView:

                  App.js
                  ...
                  import ListView from './svelte_components/ListView';
                  
                  import ProductItem from './svelte_components/ProductItem.svelte';
                  import UserItem from './svelte_components/UserItem.svelte';
                  ...
                    render() {
                      return (
                        <div className="App">
                            <ListView Item={UserItem} items={this.state.users} title="Users" />
                            <ListView Item={ProductItem} items={this.state.producsts} title="Producst" />
                        </div>
                      );
                    }
                  ...
                  


                  ListView/index.js
                  ...
                  import ListView from './ListView.svelte';
                  ...
                  const { items, Item, title } = this.props;
                  
                  this.listView = new ListView({
                                        target: this.el,
                                        data: { title, items, Item }
                                      });
                  


                  ListView.svelte
                  <h2>{title}</h2>
                  <ul>
                      {#each items as item}
                            <li>
                              <svelte:component this={Item} {...item} />
                            </li>
                      {:else}
                          <li><i>Empty list</i></li>
                      {/each}
                  </ul>
                  


                  ProductItem.svelte
                  <div class="product">{title}</div>
                  


                  UserItem.svelte
                  <div class="user">{username}</div>
                  
          +1
          image
            0

            Спасибо за статью!
            После прочтения появилось несколько вопросов:


            Серьезно, теперь мы можем использовать компоненты Svelte в нашем React приложении как совершенно обычные JS конструкторы, а значит код интеграции со Svelte ничем не будет отличаться от интеграции с любой другой standalone либой. Документация React даже содержит раздел посвященный этому: Integrating with Other Libraries .

            Всё же не понял, в чём состоит магия "исчезающего фреймворка". Что я вижу на данный момент:


            • имеется html/svelte-файл (с некоторым добавочным синтаксисом), который можно использовать в этом же html в <script> или импортировать во внешнем файле, и создать экземпляр компонента с помощью оператора new. Точно так же ведётся работа и во Vue (за исключением экземплификации).
            • Во время сборки проекта Webpack использует загрузчик для того, чтобы переварить (преобразовать) html + JS с фреймворком в бандл с VanillaJS. Но ведь Webpack делает точно то же самое и для других фреймворков, он тоже преобразовывает весь код фреймворка в VanillaJS, используя паттерны/полифиллы, так что сами фрейморки, по сути, так же исчезают.

            Обратите внимание на флажок --save-dev, помните да, что рантайма-то нету.))))

            Подскажите, пожалуйста, что здесь подразумевается под рантаймом и почему для Svelte в нём нет необходимости?

              0
              Всё же не понял, в чём состоит магия «исчезающего фреймворка».

              В таком случае, возможно, вам стоит также прочитать статью «Исчезающие фреймворки». Там, на мой взгляд, все довольно доходчиво описано.

              имеется html/svelte-файл (с некоторым добавочным синтаксисом),

              Если интересно, этот синтаксис называется HTMLx.

              который можно использовать в этом же html в script или импортировать во внешнем файле, и создать экземпляр компонента с помощью оператора new.

              Да, после компиляции, SFC Svelte превращается в обычный JS конструктор.

              Точно так же ведётся работа и во Vue (за исключением экземплификации).

              Да, по форме SFC компоненты Vue практически не отличаются от Svelte. Более того, Vue заимствовал подход SFC, а также добрую половину API из предыдущего фреймворка автора Svelte, который называется Ractive. Svelte же просто продолжает традиции Ractive.

              Во время сборки проекта Webpack использует загрузчик для того, чтобы переварить (преобразовать) html + JS с фреймворком в бандл с VanillaJS.

              Нет, это не так. Vanilla JS это когда код может работать при наличии лишь рантайма браузера. Ваши SFC в Vue все равно требуют для работы наличие самого Vue, то есть специфического рантайма.

              Но ведь Webpack делает точно то же самое и для других фреймворков, он тоже преобразовывает весь код фреймворка в VanillaJS, используя паттерны/полифиллы, так что сами фрейморки, по сути, так же исчезают.

              Ничего такого он не делает. Вебпак лишь позволяет вам использовать модульный подход в разработке, отслеживать зависимости и выполнять рутинные операции, типа минификации и сборки бандлов.

              Подскажите, пожалуйста, что здесь подразумевается под рантаймом и почему для Svelte в нём нет необходимости?

              Рантайм — это все что происходит во время выполнения. Например, в Vue прямо во время исполнения в браузере отрабатывает Virtual DOM (кстати штука, которая также одной из первых появилась в Ractive). VDOM — это довольно толстая абстракция, кроме того крайне прожорливая к памяти. В Svelte нет vdom или любого другого аналога. Он на этапе компиляции статически анализирует все связи и реактивные данные, и генерирует код прямых изменений в DOM, мимо каких либо абстракций. Примерно тоже самое он делает со всеми фичами фреймворка. В итоге в бандл попадает лишь код вашего приложения + небольшое кол-во вспомогательных хелперов.

              Вот могу еще посоветовать доклад Саши Федотова с последнего MoscowJS — «Svelte: обзор и сравнение». Он прям разжевал, мне кажется, все что нужно.
                0
                Огромное спасибо за ответ и за ссылки! Теперь стало немного яснее! :)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое