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

          imageimage

You can't write serious applications in vanilla JavaScript without hitting a complexity wall. But a compiler can do it for you.

— Rich Harris, 2016

Пролог


Зайду издалека. Примерно с 2013 года и до сих пор, я активно использую в своей работе RactiveJS — не слишком популярный, но развивающийся реактивный MVVM-фреймворк. В тот момент, когда Ractive появился, как мне кажется, он был весьма инновационен и до сих пор имеет наиболее удобный, на мой взгляд, API. Однако, как и любой другой инструмент, Ractive имеет свои минусы. Основными минусами для меня является его размер (~200Кб minified), а также не слишком высокая, по сравнению с конкурентами, скорость работы.

Относительно недавно, возникла необходимость написать довольно интерактивный виджет для сайтов (аля callback-виджет). Основные требования к виджету понятны: он должен весить мало и работать быстро. Учитывая минусы, который я описал выше, Ractive, а также большинство других JS фреймворков (Vue ~70Кб / React ~150Кб / etc.), не слишком хорошо подходят для этой задачи. Поэтому в какой-то момент даже возникла мысль писать виджет на ванильном JS, вообще без каких-либо фреймворков и библиотек. Однако мне повезло.

The magical disappearing UI framework


Svelte
Итак, SvelteJS — JS фреймворк, который компилирует свой код в vanilla javascript на этапе сборки (AoT). В связи с чем у нас полностью исчезают накладные расходы связанные с самим фреймворком. Его создателем и основным мейнтейнером является Рич Харрис (Rich Harris), автор таких прикольных штук как Rollup, Buble, Roadtrip, а также того самого Ractive. В 2016 году Рич прекратил активную работу над Ractive, оставив его другим мейнтейнерам, ради того, чтобы развивать Svelte и в том же году написал вводную статью о своей новой разработке.

В своем новом «детище» Рич попытался переосмыслить то, зачем мы вообще используем фреймворки. Почему мы готовы отправлять в браузер пользователя сотни и тысячи килобайт javascript'а, облепившись кучей абстракций над нативным кодом, по сути, убивая производительность. Если вы не считаете это проблемой, почитайте блог Алекса Рассела (Alex Russell), там довольно много интересных мыслей и примеров.

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

Почему же Svelte идеально подошел для описанной задачи?

Когда вы пишете на React, Vue или Ractive, у вас есть код вашего приложения, а также код самого фреймворка, на котором основан код вашего приложения, без которого данный код работать априори не может. Итого мы имеем код фреймворка (в среднем 100кб) + код приложения, который собственно решает задачи приложения. Однако когда вы пишете на Svelte у вас есть только код вашего приложения, потому что Svelte — это прежде всего компилятор, который создает vanilla JS из кода, написанного с использованием Svelte API. Скомпилированный код решает только задачи вашего приложения и не имеет накладных расходов. Соответственно, JS бандл весит ровно столько, сколько весит код самого приложения.

Далее, Svelte быстр. На самом деле он быстр ровно настолько, насколько быстр сам Javascript. И это очевидно, потому что Svelte — это и есть vanilla javascript, который вам не нужно писать.

Короче говоря Svelte — это средство, писать vanilla JS без необходимости писать на vanilla JS. Вот такая вот тавтология. )))))

Возможности Svelte


image
Итак, на первый взгляд, все звучит довольно аппетитно, но нужно взглянуть поближе. Сделать это проще простого!

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

Вся документация по Svelte располагается буквально на одной странице (15 min reading), также имеется готовый к использованию REPL с интерактивными примерами кода (включая 7GUIs).

Основа приложения на Svelte — это компоненты (surprise surprise). По своему виду и способу написания svelte-компоненты очень похожи на однофайловые компоненты Vue или теги Riot, да и вообще на то, что мы давно уже имеем практически во всех фреймворках, даже в Ractive. Обычно все это хозяйство основано на загрузчике (loader) для Webpack или Rollup. Со Svelte такая же история, есть svelte-loader и rollup-plugin-svelte. Также всегда можно скомпилировать компоненты в чистый JS с помощью svelte-cli.

Итак Svelte-компоненты — это просто html файл, который может иметь следующую структуру:

<!-- любая html разметка в качестве шаблона -->
<h1>Hello {{name}}!</h1>

<!-- component scoped стили -->
<style>
       h1 { color: red; }
</style>

<!-- собственное код компонента, аля ES6 модули -->
<script>
	export default {

	};
</script>

Ни один из элементов не является обязательным. Поэтому простой stepper-компонент можно написать так, вообще без script-тега:

<!-- stepper.html -->
<button on:click="set({ count: count - 1 })">-</button>
<input bind:value="count" readonly />
<button on:click="set({ count: count + 1 })">+</button>

Сам по себе Svelte крайне минималистичный. Однако при этом в нем фактически присутствует все необходимое для разработки веб-приложений.

Components & composition


Императивное создание инстанса компонента выглядит так:

import MyComponent from './MyComponent.html';

const component = new MyComponent({
  target: document.querySelector( 'main' ),
  data: {}
});

Естественно есть композиция компонентов и передача значений через аттрибуты:

<div class='widget-container'>
  <Widget foo="static" bar="{{dynamic}}" />
</div>

<script>
  import Widget from './Widget.html';

  export default {
    data () {
      return {
        dynamic: 'this can change'
      }
    },
    components: {
      Widget
    }
  };
</script>

Также имеется инъекция разметки с помощью слотов (привет Vue):

// Box component
<div class='box'>
  <slot><!-- content is injected here --></slot>
</div>

// parent component
<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

Data & computed props


Svelte-компоненты имеют внутренний стейт, который описывается в проперти «data» и должен быть POJO-объектом. Также как Ractive или Vue, Svelte-компонент может иметь вычисляемые свойства (computed props) зависящие от других реактивных данных:

<p>
  The time is
  <strong>{{hours}}:{{minutes}}:{{seconds}}</strong>
</p>

<script>
  export default {
    data () {
      return {
        time: new Date()
      };
    },
    computed: {
      hours: time => time.getHours(),
      minutes: time => time.getMinutes(),
      seconds: time => time.getSeconds()
    }
  };
</script>

Единственный минус вычисляемых свойств в Svelte — отсутствие возможности задать setter для них, т.е. вычисляемые свойства работают только на получение значения.

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

component.set({ time: new Date() });
component.get('time');

При изменении данных компонента с помощью метода set(), изменение реактивно распространяется на все зависящие вычисляемые свойства и DOM.

Observers & Lifecycle hooks


Как я уже говорил, Svelte минималистичен, поэтому предоставляет всего 2 lifecycle хука: oncreate и ondestroy. Изменение данных, мы можем отслеживать с помощью наблюдателей:

<script>
  export default {
    oncreate () {
        const observer = this.observe('foo', (newVal, oldVal) => {});
        this.on('destroy', () => observer.cancel());
    }
  };
</script>


Events


Svelte имеет полноценную систему прокси-событий (DOM Events proxy) и кастомных событий (Custom Events), которые также могут быть применены к компонентам:

<!-- CategoryChooser.html -->
<p>Select a category:</p>
{{#each categories as category}}
  <button on:click="fire('select', { category })">select {{category}}</button>
{{/each}}

<script>
  export default {
    data() {
      return {
        categories: [
          'animal',
          'vegetable',
          'mineral'
        ]
      }
    }
  };

<!-- parent component -->
<CategoryChooser on:select="doSomething(event.category)"/>
</script>

Здесь мы подписываемся на клик по кнопке, преобразуем DOM событие клика в кастомное событие компонента «onselect». Далее, родительский компонент может подписываться на событие «onselect» данного компонента. Короче говоря кастомные события также умеют «всплывать» (bubbling). Все как обычно.)))

API для работы с событиями также минималистично:

const listener = component.on( 'thingHappened', event => {
     console.log( `A thing happened: ${event.thing}` );
});
// some time later..
listener.cancel();

component.fire( 'thingHappened', {
  thing: 'this event was fired'
});

Templating & directives


Синтаксис шаблонов Svelte очень напоминает «усы», однако по факту ими не является:

<!-- значения могут использоваться как в виде текста, так и внутри аттрибутов -->
<h1 style="color: {{color}};">{{color}}</h1>
<p hidden="{{hideParagraph}}">You can hide this paragraph.</p>

<!-- поддерживаются условия -->
{{#if user.loggedIn}}
  <a href='/logout'>log out</a>
{{else}}
  <a href='/login'>log in</a>
{{/if}}

<!-- а также циклы -->
<ul>
  {{#each list as item}}
    <li>{{item.title}}</li>
  {{/each}}
</ul>

В Svelte нет кастомных директив (во всяком случае пока), однако имеется несколько видов директив «из коробки»:

<!-- Event handlers -->
<button on:click="set({ count: count + 1 })">+1</button>
<!-- Two-way binding -->
<input bind:value="count" />
<!-- Refs (like Vue) -->
<canvas ref:canvas width="200" height="200"></canvas>
<!-- Transitions -->
<div transition:fly="{y:20}">hello!</div>

Не уверен, что кастомные директивы вообще появятся. Все же похоже что этот «AngularJS-style» подход потихоньку себя изживает.

Custom methods & helpers


Методы инстанса компонента и хелпер-функции для шаблонов можно определить так:

<script>
  export default {
    helpers: {
       toUppeCase: (str) => {}
    },
    methods: {
      toast: (msg) => {}
    }
  };
</script>

Plugins


Судя по всему в Svelte есть какие-то наработки по системе плагинов. Правда документация пока умалчивает об этом. Те же Transitions реализованы в виде отдельных модулей и собраны в отдельный пакет svelte-transitions. Очевидно можно писать свои собственные переходы.

Тоже есть отдельный пакет с дополнительными методами для инстанса компонента (svelte-extras), которые можно подгружать отдельно, приближая API Svelte к тому же Ractive:

ractive.set('list.3.name', 'Rich');

// базоый set() не умеет устанавливать вложенные значения напрямую
svelte.setDeep('list.3.name', 'Rich');

Вообще система плагинов пока не слишком развита или во всяком случае описана в документации. Ждемс.

SSR


Серверный рендеринг также имеется, правда работает он довольно странно, по сравнению с тем же Ractive:

// просто и не принужденно в Ractive
const html = ractive.toHTML();

// немного более замороченно в Svelte
require( 'svelte/ssr/register' );
const html = svelte.render( data );

// css также можно отрендерить на сервере
const styles = svelte.renderCss();

State management


У Svelte есть своя интерпретация механизма хранилища, которая с одной стороны имеет некоторые сходства с Redux/Vuex, однако отличий все же больше. Подробнее можно почитать в документации, однако основное отличие заключается в том, что Svelte Store делает акцент не на механизме изменения стейта (иммутабильность, one-way data flow и все такое), а на механизме обмена (шеринга) стейта между компонентами. К примеру, использование «store» вместо «data» в компоненте верхнего уровня приведет к тому, что данные хранилища будут автоматически доступны всем child-компонентам. Честно говоря, мне не довелось еще попользовать этот механизм, но насколько я понимаю, это что-то вроде глобального стейта в пределах одной иерархии компонентов, а не всего приложения.

Также в Svelte Store нет никаких экшенов и коммитов. Однако, если такой механизм управления доступом необходим, разрешается расширять класс Store, для имплементации подобной логики.

Бонус


image

Выше я кратко описал основные возможности Svelte. Учитывая его минималистичность, а также фактически встроенный «tree-shaking» (компилируется всегда только то, что реально используется), возможности Svelte выглядят весьма не плохо.

Однако, есть еще несколько прикольный штук, которым стоит уделить отдельное внимание.

Async support


Начну с моего любимого — нативная поддержка асинхронных значений на основе промиссов. Выглядит это так:

{{#await promise}}
  <p>loading...</p>
{{then data}}
  <p>got data: {{data.value}}</p>
{{catch err}}
  <p>whoops! {{err.message}}</p>
{{/await}}

Это офигенно упрощает жизнь. В Ractive мы тоже можем делать подобные штуки, правда для этого приходится пользоваться специальным Promise-adaptor. Svelte дает это из коробки.

Namespaces


Штука которую пока нигде больше не видел — с помощью неймспейсов можно определять область применения компонента. Например, если наш компонент применим только для использования внутри svg-тега, мы можем так и указать:

<g transform='translate({{x}},{{y}}) scale({{size / 366.5}})'>
  <circle r="336.5" fill="{{fill}}"/>
  <path d="m-41.5 298.5c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
</g>
<script>
  export default {
    namespace: 'svg'
  };
</script>

Интересная возможность. Пока не до конца ясно, насколько это реально полезно, однако определенно есть перспективы.

<:Self> tags


Также необычная фича — рекурсивное включение компонента:

{{#if countdown > 0}}
  <p>{{countdown}}</p>
  <:Self countdown='{{countdown - 1}}'/>
{{else}}
  <p>liftoff!</p>
{{/if}}

Довольно неожиданная реализация countdown счетчика, не правда ли? )))

<:Window> tags


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

<:Window on:keydown='set({ key: event.key, keyCode: event.keyCode })'/>

Sapper


Об этом проекте узнал буквально вчера. Проект пока не более чем наброски идей, однако похоже что Рич собирается делать что-то вроде аналога Next.js/Nuxt.js. Уверен, что если все получится, аналог будет сделан с присущей Ричу долей индивидуальности.

Эпилог


Спасибо всем тем, кто дочитал до конца. Как говориться, ставьте лайки и подписывайтесь на мой канал и да прибудет с вами Сила!

Если серьезно, надеюсь мне удалось в общих чертах рассказать о таком интересном явлении как Svelte. От себя могу добавить, что использование Svelte в уже двух проектах полностью себя оправдало. Лично мне немного не хватает богатства возможностей Ractive, однако минималистичность, а также сам принцип работы Svelte сильно подкупает использовать его и дальше. Сейчас серьезно подумываю заюзать его в новом, довольно крупном проекте и со временем возможно полностью перебраться с Ractive на Svelte.

Надеюсь вы тоже откроете для себя Svelte. Удачи!

UPDATE:


Спасибо serjoga за дополнение. StencilJS похоже идет по тому же пути, что и Svelte, только у него более сложный, «Angular2-style» синтаксис и JSX.

Комментарии (48)


  1. vlreshet
    19.12.2017 15:05

    Не очень понятно — а в чём выигрываем то? Смотрю официальный пример — на 1 строчку html-шаблона сгенерировало 237 строк кода. Какая в итоге разница — тащить фреймворк отдельно, или вот так? Он же всё равно никуда не изчезает.

    upd: дописал ещё одну строку в шаблон, и 3 новых переменных — оп, плюс 40 строк кода. Причём весьма «тупого», копипастного кода. А если переменных будет 30? А 40? Не похоже на заметный выигрыш, генерировать код вместо переиспользования. Выглядит как шаг назад.


    1. PaulMaly Автор
      19.12.2017 20:10

      Вы же не поверили в заголовок и не ждали какой-то реальной «магии», правда? Совсем без кода конечно не получается)))

      Есть некоторые вопросы с дублированием кода, это правда, но они решаемы и решаются. К тому же не нужно забывать, что это довольно смелая идея и по-сути первая разработка подобного рода. Уверен, что проблемы, которые наблюдаются сейчас будут решены в будущем, код будет только оптимизироваться, а статический анализатор улучшаться. Уже есть много идей насчет 2-й версии (текущая 1.4). Тот же Vue тоже не сразу стал таким крутым, как сейчас, первая версия была не очень.

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

      Ну и опять же давайте смотреть реальные примеры:
      ToDoMVC Svelte (http://svelte-todomvc.surge.sh/) ~ 3Kb
      ToDoMVC Vanilla (http://todomvc.com/examples/vanillajs/) ~ 11Kb
      ToDoMVC Vue (http://todomvc.com/examples/vue/) ~ 80Kb
      ToDoMVC React (http://todomvc.com/examples/react/) ~ 300Kb

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


    1. PaulMaly Автор
      23.12.2017 00:56
      +1

      Специально уточнил у Рича ответ на ваш вопрос:

      Не очень понятно — а в чём выигрываем то? Смотрю официальный пример — на 1 строчку html-шаблона сгенерировало 237 строк кода. Какая в итоге разница — тащить фреймворк отдельно, или вот так? Он же всё равно никуда не изчезает.


      Оверкост на каждый компонент в данный момент составляет буквально вот такой кусок кода:
      function SvelteComponent(options) {
          init(this, options);
          this._state = assign({}, options.data);
          this._fragment = create_main_fragment(this._state, this);
          if (options.target) {
              this._fragment.c();
              this._fragment.m(options.target, options.anchor || null);
          }
      }
      assign(SvelteComponent.prototype, proto);
      export default SvelteComponent;
      


      Рич, говорит что у него есть идеи, чтобы минимизировать и этот кусок кода + минимизировать некоторые функции, которы используются.

      В официальных примерх компоненты компилируются и отображаются с полном объеме для наглядности. Иными словами, они там скомпилированы с флагом shared: false.

      Спасибо, за ваш вопрос. Мне стало самому интересно.


  1. k12th
    19.12.2017 15:24

    Честно говоря, вот это вот "исчезающий фреймворк" кажется маркетинговой уловкой. *.vue компоненты не компилируются в чистый JS? JSX не компилируется в чистый JS? Ну и понятно, что все эти component.set все равно требуют каких-то вспомогательных функций и они попадут в бандл.


    Однако минималистичное API и небольшой размер в любом случае приятно.


    1. vlreshet
      19.12.2017 16:13

      Да честно говоря и вся эта гонка за «невидимым» кодом тоже не очень то имеет смысл. Воот, мол, vue весит 70кб, реакт 150, а здесь всего 20 (например). Только а толку от экономии на спичках если полноценное приложение в любом случае натянет ещё шрифтов на пару сотен килобайт, стилей на такие же размеры, и изображений на пару мегабайт. Смешивать логику приложения и логику абстракции чтобы единоразово (потом заработает кэш) съэкономить 100мс на загрузке фреймворка — ну такой себе выигрыш.


      1. k12th
        19.12.2017 16:17

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


        1. vlreshet
          19.12.2017 17:34
          +1

          В итоге для небольшого приложения надо будет хранить «исходники», скомпилированную версию, сам компилятор, а в худшем случае ещё и бандлер какой-нибудь который будет всё это дело собирать. ИМХО, лучше тогда тот же vue использовать. Один скрипт подключил (хоть с CDN), и погнал.


          1. PaulMaly Автор
            19.12.2017 21:27

            Зачем вам хранить что-то? Храните исходники. Компилятор хранится в npm и github. Скомпилировать можно всегда когда понадобиться. Скомпилированный vanilla JS кладите хоть на CDN, хоть куда. Без зависимостей.


            1. vlreshet
              20.12.2017 10:30

              Зачем вам хранить что-то? Храните исходники. Компилятор хранится в npm и github.
              Эээ нее, в мире js я бы так не рисковал. В мире js всё слишком быстро меняется, и нет никакой гарантии что через условный год этот фреймворк не обновится сломав всякую обратную совместимость. А в npm не придёт какой-нибудь правообладатель имени и пакет не удалят (вспоминаем left-pad). И что потом делать? Плясать с бубном и искать в гите старые версии?


              1. PaulMaly Автор
                20.12.2017 11:23

                Плясать с бубном и искать в гите старые версии?

                В смысле искать? Это же гит. Вы можете оттуда поставить любую версию, вплоть до конкретного коммита.

                Если так рассуждать, тогда вообще фреймворками и сторонними либами лучше не пользоваться. Пишите все сами.


      1. PaulMaly Автор
        19.12.2017 21:24

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

        Отвечу словами Рича из его вводной статьи:
        We're shipping too much code to our users. Like a lot of front end developers, I've been in denial about that fact, thinking that it was fine to serve 100kb of JavaScript on page load – just use one less .jpg! – and that what really mattered was performance once your app was already interactive.

        But I was wrong. 100kb of .js isn't equivalent to 100kb of .jpg. It's not just the network time that'll kill your app's startup performance, but the time spent parsing and evaluating your script, during which time the browser becomes completely unresponsive. On mobile, those milliseconds rack up very quickly.

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

        Смешивать логику приложения и логику абстракции чтобы единоразово (потом заработает кэш) съэкономить 100мс на загрузке фреймворка — ну такой себе выигрыш.

        А где вы увидели смешение? Svelte API вполне себе соответствует самым распространенным подходам в написании компонентов. Примеры есть в статье.


    1. PaulMaly Автор
      19.12.2017 20:18

      Позволю себе не согласиться с вами. Vue-компоненты и JSX все же компилятся не в ванильный JS. Вы не можете скомпилить Vue-компонент и положить его на CDN или внедрить в приложение на React без того, чтобы тащить Vue в зависимостях.

      Svelte генерирует полностью ванильный JS. На самом деле вы уже сейчас можете написать какой-то компонент на Svelte, скомпилить его и безшовно внедрить в существующее приложение на Vue. Также как вы, я уверен, не раз внедряли туда D3 или какую-то другую стороннюю JS либу на ваниле.

      Ну и понятно, что все эти component.set все равно требуют каких-то вспомогательных функций и они попадут в бандл

      Конечно потребуется, совсем без кода не бывает. Отличие в том, что в бандл попадет лишь то что реально используется, а не вся махина фреймверка. Как я писал в статье, это фактически «tree-shaking» из коробки. Сейчас он может работать лучше, или хуже, но в итоге Svelte всегда будет стремиться к оптимизации генерируемого кода. В этом его суть.


      1. k12th
        19.12.2017 21:21

        написать какой-то компонент на Svelte, скомпилить его и безшовно внедрить в существующее приложение

        Хм, а как это будет выглядеть? Я имею ввиду, как мне передать в этот svelte-компонент параметры и получить от него вывод?


        1. PaulMaly Автор
          19.12.2017 21:31

          Код который генерит Svelte экспортирует вам конструктор. Типа того:

          // svelte-component.js
          ......
          export default App;
          
          // your file
          import SvelteComponent from 'svelte-component.js'
          
          const comp = new SvelteComponent({});
          


  1. Zuki
    20.12.2017 03:04

    Какая, однако, интересная штука! Для меня выглядит как глоток свежего воздуха на фоне жирнющих фреймворков.
    Надеюсь, что в итоге авторам удастся добиться компиляции максимально эффективного кода.:)


  1. riky
    20.12.2017 05:20

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

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


    1. PaulMaly Автор
      20.12.2017 11:24

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

      Это дело наживное)) Будет интерес к фреймворку — будут и куча готовых компонентов.


      1. riky
        20.12.2017 15:41

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

        но для отдельных компонентов которые будут встраиваться в разные сайты, которые непонятно на чем написаны и что есть из коробки (CMS), идеально подойдет. Сам буду следить за проектом именно для этих целей. Если делать сайт полностью — то лучше возьму полноценный VUE. По крайней мере пока ощущения такие.


        1. PaulMaly Автор
          20.12.2017 15:47

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

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


  1. DarkGenius
    20.12.2017 08:42

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


    1. PaulMaly Автор
      20.12.2017 11:25

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


      1. godlin
        20.12.2017 22:46

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


        1. PaulMaly Автор
          22.12.2017 08:42

          Для этого как бы и придуман Sourse Mapping.


  1. serjoga
    20.12.2017 10:31

    https://stenciljs.com/ попадает в ту же категорию, от разработчиков Ionic.


    1. PaulMaly Автор
      20.12.2017 11:29

      Видел его также, но он пока даже «не показался на поверхности». Его и в JS Frameworks Benchmark последнем нет, хотя там есть такие штуки, о которыя лично я первый раз вообще слышал. Но все равно спасибо что дополнили статью ссылкой, пожалуй помещу ее в конец статьи.


      1. serjoga
        20.12.2017 12:34

        Они переписывают Ionic на stenciljs и потом Ionic можно будет использовать с любым фреймворком.
        Думаю они пока не парятся за популярность stencil, так как Это просто сайд продукт


  1. vintage
    20.12.2017 11:42

    1. PaulMaly Автор
      20.12.2017 11:47

      Итересный факт, что судя по вашему бенчмарку Ractive быстрее Vue, однако JS Frameworks Benchmark говорит об обратном. Есть идеи?


      1. vintage
        20.12.2017 19:42

        В чём-то быстрее, в чём-то медленнее.


        JSFB — обычная писькомерка, она ничего не скажет об отзывчивости приложения, которое складывается далеко не только из рендеринга.


        Ну и да, $mol тоже не бандлит ничего лишнего — только то, что необходимо (todomvc ~10kb). При этом полностью статически типизирован в отличие от большинства JS поделок и сабжа в том числе. То есть на нём можно делать, как маленькие встраиваемые виджеты, так и огромные сложные приложения. Не "выбирая инструмент под задачу", а по мере усложнения задачи не переписывая на более сложный инструмент. Библиотека стандартных легко кастомизируемых виджетов в комплекте. Ну и, конечно, уникальная киллер-фича — ленивый рендеринг, который позволяет быстро показать страницу, даже вывалив на неё кучу данных в условиях ограниченных ресурсов.


        Svelte — не более чем DSL, с соответствующими проблемами — среда разработки совершенно не понимает, что вы пишите. Поэтому фреймворк лучше, чем DSL, но только если он микромодульный, а не монолитный.


        1. PaulMaly Автор
          21.12.2017 13:08
          +1

          Ну и да, $mol тоже не бандлит ничего лишнего


          Ну то что $mol лучший, это и так понятно. ;) Я вообще считаю, что у $mol только один минус — им никто не пользуется и никто не будет пользоваться. В остальном он великолепен!

          Кстати, а вы могли бы добавить в свою «пискомерку» вот эту либу: http://leeoniya.github.io/domvm/demos/TodoMVC/? Буду вам очень признателен.


        1. redyuf
          21.12.2017 13:32

          Локализации бандлит же, т.к. они прибиты гвоздями к mol_view


          1. vintage
            22.12.2017 01:14

            Не прибиты. Да я уже и убрал их из ToDoMVC, ибо всё-равно переводов кроме английских там не было.


            PaulMaly добавлю как будет время. Ну или можете пул-реквест сделать, чтобы это произошло быстрее: https://github.com/eigenmethod/todomvc


  1. redyuf
    20.12.2017 14:45

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

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

    Это выглядит как очередная стадия взросления экосистемы, где пока нет нормальных стандартов, компилятора с нормальным tree shaking и языка с мощными возможностями метапрограммирования.

    Почему так на этой производительности все сосредоточились: react и vue далеко не самые быстрые, но пока в большинстве задач их производительности хватает.

    Если отбросить оптимизацию, которая часто приводит к увеличению объема кода, чем принципиально такая кодогенерация лучше обычной сборки какого-нибудь todomvc на preact ролапом (кода кстати тоже около 3кб в gz)?


    1. PaulMaly Автор
      20.12.2017 14:57

      В целом могу с вами согласиться, но имеем то что имеем.

      Почему так на этой производительности все сосредоточились: react и vue далеко не самые быстрые, но пока в большинстве задач их производительности хватает.

      Ведь я же неспроста «начал издалека»)) В статье четко указана причина, по которой я вообще начал искать альтернативное решение. Все дело в том, что я не считаю (и мой заказчик тоже), что виджет, который будет устанавливаться на сайты клиентов, должен тащить с собой 100Кб фреймверка, при весе самого виджета в несколько Кб. Ну и скорость конечно тоже играет роль.

      Касательно производительности, часто работаем со Смарт ТВ (одно из направлений деятельности), так вот там Ractive частенько подтормаживает на бюджетных теликах. Последний проект делали на Svelte результаты порадовали. Учитывая схожешь API, возможно даже перепишем часть приложений на него.

      Если отбросить оптимизацию, которая часто приводит к увеличению объема кода, чем принципиально такая кодогенерация лучше обычной сборки какого-нибудь todomvc на preact ролапом (кода кстати тоже около 3кб в gz)?

      Принципиально ничем наверное. Хотя 3Кб весит только сам preact, насколько я помню. Плюс код, плюс шаблоны и того больше получится скорее всего. Собственно вот — 6Кб, те больше ровно на вес фреймверка. Такие пироги.)))


      1. redyuf
        20.12.2017 15:16

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

        А вот кодогенерация создает некоторый избыточный код на каждый компонент. Да, svetle-todomvc 22кб без сжатия и минификации, но код изобилует портянками createElement/appendNode, т.е. низкоуровневой работой с DOM, которая слабо реюзается, на каждый if в шаблоне тоже генерятся приличные портянки функций.

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


        1. PaulMaly Автор
          20.12.2017 15:35
          -1

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

          Если вы прочитали статью, то наверное заметили, что Svelte кроме всего прочего обладает значительно большими возможностями, чем Preact и даже React. Если фреймверк маленького размера это как правило сказывается и на функциональности.

          Подход Svelte гарантирует, что каким бы огромным и функциональным не был сам Svelte, вы не будете тащить все это добро к себе в бандл. Только то, что нужно.

          но код изобилует портянками createElement/appendNode, т.е. низкоуровневой работой с DOM, которая слабо реюзается, на каждый if в шаблоне тоже генерятся приличные портянки функций.

          Вообще, это как бы и называется ванилла JS.))) Вы если бы писали на ванилле, не использовали бы DOM API и т.п.?

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

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


          1. mayorovp
            20.12.2017 15:40

            К ванилле JS есть множество плагинов, использование которых может сократить объем кода (обычно увеличивает, да — но если подойти с умом то может и сократить).


            1. PaulMaly Автор
              20.12.2017 15:49

              Ванилла JS не имеет системы плагинов))) Вы наверное про либы на ванилле, но в итоге вы просто соберете «фреймворк-солянку» )))


              1. mayorovp
                20.12.2017 15:51

                Там на странице проекта внизу есть приписка


                or try out one of the many Vanilla JS plugins.

                Я говорил именно об этих "плагинах" :-)


                1. PaulMaly Автор
                  20.12.2017 16:08

                  Т.е. под «плагинами», вы имели ввиду фреймворки? Хитро закрутили мысль, я и не понял))


          1. redyuf
            20.12.2017 16:20

            большими возможностями, чем Preact и даже React.
            Ну да, логичнее с vue сравнивать.
            Если фреймверк маленького размера это как правило сказывается и на функциональности.
            КПД фреймворка зависит от идеи в его основе, тут уж кто как угадает. Мне кажется, если автор смог такой генератор написать, то смог бы и обычный фреймворк не хуже, только tree shaking friendly.
            Вообще, это как бы и называется ванилла JS.))) Вы если бы писали на ванилле, не использовали бы DOM API и т.п.?
            Я бы наделал хелперов над DOM, т.к. он слишком низкоуровневый.
            Писал где-то выше. Любое дублирование кода, можно оптимизировать
            Посмотрим как у автора получится. Мне это видится более сложной задачей, чем написать нормальный фреймворк. Опосредованно получается, мы не сразу пишем оптимальный код фреймворка, а пишем генератор, который должен давать оптимальный код.


            1. PaulMaly Автор
              20.12.2017 16:34

              Ну да, логичнее с vue сравнивать.

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

              КПД фреймворка зависит от идеи в его основе, тут уж кто как угадает. Мне кажется, если автор смог такой генератор написать, то смог бы и обычный фреймворк не хуже, только tree shaking friendly.

              Предыдущее детище автора — RactiveJS, весьма не плох, по сравнению с остальными, в плане функциональных возможностей. Насчет «tree shaking friendly», что вы имеете ввиду? Как мне кажется эффективный «tree shaking» прежде всего зависит от применения модульности в итоговом коде приложения, а не от фреймворка.


              1. redyuf
                20.12.2017 17:39

                Ну да, но при этом весь результирующего кода Svelte и Vue в десятки раз меньше.
                Нужно объективно сравнивать степень автоматизации в реальных проектах, в todomvc нет даже обработки ошибок и серверного взаимодействия.

                Мне в реальности нужно знать насколько хорошо фреймворк автоматизирует
                1. Реактивность, как сделан observable state, автотрекинг зависимостей без on/off/subscribe/observe (mobx, cellx, mol_atom), работа с сервером: крутилка, обработка ошибок, retry
                2. Контексты, DI или что-то вроде, что бы не прокидывать все через пропсы
                3. Компоненты, как расширить уже имеющийся компонент (принцип open/close), переиспользовать с другой копией стейта
                4. Как задать стили компонента и стили компонента в контексте чего-либо, как динамически управлять стилями и темизацией из js
                5. Типизация (насколько хорошо используется вывод типов вместо прописывания аннотаций, работает ли в шаблонах/стилях?)

                Svetle простая штука для небольших проектов, но тут пока до уровня vue ему далеко.

                Хотелось бы конечно видеть полноценный todomvc (вроде такого) и сравнивать, насколько вышеперечисленные фишки просто делаются в коде.

                Насчет «tree shaking friendly», что вы имеете ввиду?
                Проблема отбрасывания лишнего кода техническая и она в природе импортов в js, поэтому приходится особым образом собирать и использовать эти модули. Это и есть friendly =).

                Например, сейчас часто модули фреймворка собираются вместе в index.js, что мешает tree shaking-у. Но, в том же lodash, мы же можем так сделать: import _add from 'lodash/fp/add', есть даже плагин, которые такие импорты делает из обычного import _ from 'lodash'.


                1. PaulMaly Автор
                  21.12.2017 13:07

                  Мне в реальности нужно знать насколько хорошо фреймворк автоматизирует

                  Вас так послушать, так только Angular вам подходит. )))) Svelte, Ractive, React и Vue — это все же библиотеки UI, а не полноценные MV* фреймверки.

                  Svetle простая штука для небольших проектов, но тут пока до уровня vue ему далеко.

                  А что такого принципиального умеет Vue, что нельзя реализовать на Svelte?

                  Например, сейчас часто модули фреймворка собираются вместе в index.js, что мешает tree shaking-у. Но, в том же lodash, мы же можем так сделать: import _add from 'lodash/fp/add', есть даже плагин, которые такие импорты делает из обычного import _ from 'lodash'

                  Не, вы меня не поняли. Я к тому, что даже если либа молодец, все распихала по отдельным подмодулям и все такое (как lodash к примеру), но так как фича tree shaking'a основана на импортах, запороть ее может уже сам разработчик, использующий данный модуль. Например, тупо используя дефолтный конфиг Babel. Все, на этом tree shaking сразу заканчивается.


    1. vlreshet
      20.12.2017 15:01

      ИМХО, очередная стадия развития будет тогда когда браузерный JS будет уметь в неймспейсы и автолоадинг (ну или что-то в этом роде). Тогда не надо будет компиляторов для того чтобы сгребать всё в один js файл, тогда не надо будет tree shaking (будет подтягиваться только то что реально используется, и никакого статического анализа не надо), не надо будет кучи обёрток над обёртками в замыканиях, чтобы избежать пересечений. Я сужу по PHP, качественный скачок у которого произошёл именно в тот момент когда появились неймспейсы и автолоад, и с того времени фреймворки стали действительно слабо связанным набором инструментов, а не лапшой из бесконечных require_once и Вот_Таких_вот_Классов.


      1. PaulMaly Автор
        20.12.2017 15:14

        Как уже писал выше, готов согласиться с первой частью. И все же серверный код и клиентский код это разные вещи. Не думаю что гонка за соотношением «размер приложения — размер бандла» когда-нибудь закончится. Во всяком случае, пока у нас существуют сеть и связанные с передачей по сети проблемы. Так как и никогда не закончится гонка «сложность приложения — скорость работы», потому как на сервере у вас полностью подконтрольное окружение, а разнообразие клиентов всех мастей, только растет с годами. Скоро будем на картошке JS запускать.


  1. igormich88
    20.12.2017 15:17

    Чем меня смущают компилирующие фреймворки для js, так это тем что они существенно усложняют отладку кода, хотя есть конечно Sourse Mapping. Не подскажите как тут с этим обстоят дела?


    1. PaulMaly Автор
      20.12.2017 15:37

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