Как стать автором
Обновить

Комментарии 12

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


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

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

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

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


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

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


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


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

Да не, такое тоже можно сделать. 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 были максимально изолированы, но все же сделать такое можно.

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


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


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 — авантюра серьезная, было бы неловко узнать спустя полгода разработки, что нельзя реализовать запрошенный компонент из-за ограничений платформы.

Как слоты могут быть статическими? 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

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


<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 не может быть использован в цикле, поэтому такой номер не прокатывает.

А, теперь понял ваш пример. Только вопрос какой смысл в таком использовании слотов, если вы ничего не можете сделать с 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>
НЛО прилетело и опубликовало эту надпись здесь

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


Серьезно, теперь мы можем использовать компоненты 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 в нём нет необходимости?

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

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

имеется 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: обзор и сравнение». Он прям разжевал, мне кажется, все что нужно.
Огромное спасибо за ответ и за ссылки! Теперь стало немного яснее! :)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории