Хабрахабр

RE: Боль и слёзы в Svelte 3

Вместо предисловия

Данный пост является ответом на вчерашнюю статью «Боль и слёзы в Svelte 3» и появился как следствие сильно «располневшего» комментария к оригинальной статье, который я решил оформить в виде поста. Ниже я буду использовать слово автор для отсылки к автору оригинальной статьи и позволю себе сделать некоторые уточнения по всем пунктам. Поехали!

Кто такой Svelte?

Когда я увидел заголовок оригинальной статьи, сперва очень обрадовался. Вот сейчас, думаю, прочитаю какую-то глубокую и конструктивную критику. А главное интересные кейсы с подводными камнями не от «диванных экспертов», а от ребят «которые смогли». После прочтения, энтузиазма поубавилось, но все равно большое спасибо vds64_max за статью.

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

Tutorial

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

UI Kit и Стили

Хотелось воскликнуть: «Хоть Material, Bootstrap… хоть что нибудь...». Поиски UI Kit для Svelte были отдельной болью для всех нас.

Во-первых, далеко не во всех проектах в принципе применимы UI киты. Во-вторых, далеко не все UI киты для других фреймворков работают хорошо.

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

По ссылке можете ознакомиться и с другими UI китами для Svelte.

Например простой спиннер отображается через раз: Из-за того, что Svelte работает с DOM «по другому» в MaterialUI начали вылазить всякие гадости, связанные с тем как отображаются UI компоненты которые добавляются через js в dom.

Так и не понял, что означает начало предложения и как именно «по-другому» работает Svelte с DOM, но в целом весь тезис звучит, по крайней мере, не серьезно. Если вы попробуете интегрировать стороннюю либу работающую с DOM в любой из фреймворков (React/Vue/Angular/Ember), управляющих DOM, у вас будут всплывать точно те же вопросы. Сразу возникает ощущение, что автор никогда не делал этого.

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

Для примера, имплементация MDCSlider за 2 минуты: REPL


Скажите, пожалуйста, куда уж проще?

Стилизация

Вы пишите стиль и все в порядке, и потом пишите компонент UI (так как у Вас нет UIKit) который должен принимать параметры props, например width и height, и логично делаете это так:

А… нет, в стиле у Вас не получится вставлять переменные.
Со стилями все предельно ясно, мы запихиваем все стили в как в Vue.

Насколько мне известно, во Vue стейт также не может использоваться внутри стилей компонента, а в React вообще нет работы со стилями из коробки.

Хотя, я могу понять желание использовать динамику в стилях, но есть ряд причин почему так делать не надо:

  • Как только кусок стейта компонента появляется в стилях эти стили надо дублировать для КАЖДОГО инстанса компонента. Сразу представляем список из 1000 айтемов со сложным дизайном. Оно нам надо?
  • Использование динамики в css вызывает много лишних перерисовок и крайне не производительно.

Альтернативные способы использовать динамические стили в Svelte:

  • Смена классов с помощью директивы class:
  • Использование инлайн стилей с помощью атрибута style
  • Использование css custom properties (variables)
  • Использование css-in-js решения, таких как Emotion (статья на эту тему)

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

<button v-on:click="fontSize++">Increase font size</button>
<button v-on:click="fontSize--">Decrease font size</button> <p v-bind:style="">Font size is: {{ fontSize }}</p>

<button on:click={e => fontSize++}>Increase font size</button>
<button on:click={e => fontSize--}>Decrease font size</button> <p style="font-size: {fontSize + 'px'}">Font size is: {fontSize}</p>

REPL

Странно это. В целом, особых отличий я не вижу, но автор почему-то считает что в Vue все с этих хорошо, а в Svelte нет.

Кстати, с помощью реактивных деклараций, можно довольно удобно собирать все динамические стили в одном месте:

<div {style}>....</div> <script> let fontSize = 10; let color = 'red'; let width = 50; $: style = ` font-size: ${fontSize}px; color: ${color}; width: ${width}px; `;
</script>

Обычно этого хватает для 99% случаев, а остальное решается с помощью тем и/или css custom properties.

Routing и ротуеры

Вообще, Svelte — это UI фреймворк и он агностичен к роутеру. Более того, внешний апи Svelte позволяет легко интегрировать любой независимый роутера буквально за пару минут и строк кода. На примере page.js:

import page from 'page'; import App from './App.svelte'; const app = new App({ target: document.body }); page('/posts', ctx => { // можно даже сразу с dynamic import & code-splitting app.$set({ ctx, page: import('./Posts.svelte') });
}); page('/', ctx => { app.$set({ ctx, page: import('./Home.svelte') });
}); page.start(); export default app;

<nav> <a href="/">Home</a> <a href="/posts">Posts</a>
</nav> <main>
{#await page} <p>Loading...</p>
{:then comp} <svelte:component this={comp.default || comp} {...ctx} />
{/await}
</main> <script> export let page = null, ctx = null;
</script>

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

Ошибки

При этом если Вы забыли что-то npm install'нуть или export'ануть, все отлично соберется и просто покажет белый экран (Кстати говоря так было в первых версиях React). В отличии от React который просто не даст Вам создать bundle и будет сильно «кричать» об ошибке, Svelte отлично соберёт всё с ошибкой.

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

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

Svelte просто необходим naming convention

C этой проблемой Вы будете сталкиваться довольно часто — с отсутствием конвенции по наименованию.

Очень понравилась эта фраза, потому что по-сути ее можно использовать для любого проекта/библиотеки/языка программирования. Лукавство здесь заключается в том, что точно также вы будете сталкиваться с этим везде. Например, в том же React:

class MyComponent extends React.Component { constructor(props) { super(props); this.dialog = null; this.loading = false; this.pins = []; this.stitch = null; } render() { return <dialog ref={el=> this.dialog = el} class="mdl-dialog» />; }
}

В общем и целом все тоже самое и нужно будет как-то «понимать» что есть рефка на элемент, а что есть объект для работы с бекендом или флажок загрузки. Казалось бы, если у вас команда, которая собралась не вчера, все эти вопросы давно должны быть решены.

Некоторые вещи которые использую в своих проектах: В целом конечно, если у вас в проекте и команде нет naming convention тут вам Svelte ничем не поможет.

<!-- шаблон сверху, хотя в доках советуют внизу -->
<Modal {open}> <!-- использовать шорткаты как можно чаще --> <!-- использовать модификаторы, чтобы отвязать функции от ивентов, там где это имеет смысл --> <form on:submit|preventDefault={save}> <!-- сначала директивы, потому атрибуты --> <input bind:value={firstName} placeholder="First name"> <input bind:value={lastName} placeholder="Last name"> <input bind:value={birthDay} use:asDatepicker placeholder="Birthday"> <button bind:this={btnEl}>Save</button> </form>
</Modal> <button on:click={e => open = true}>Open</button> <!-- далее логика -->
<script> // группируем импорты по типам // компоненты - это классы, поэтому с большой буквы import Modal from '~/components/Modal'; // для экшенов использую префик `as`, получается читаемо `use:as<something>` import asDatepicker from '~/actions/datepicker'; // так как сторы - это observerable, использую нейминг из cycle/rx import user$ from '~/stores/user'; import { saveUser } from '/api'; // группируем переменные по смыслу let firstName = '', lastName = '', birthDay = ''; let open = false; let btnEl; // для рефов на элементы или компоненты можно давать понятные имена let refs = {}; // либо просто хранить их в объекте async function save() { const patch = { firstName, lastName, birthDay }; await saveUser(patch); $user$ = { ...$user$, ...patch }; } // мне нравится видеть весь апи компонента в одном месте export { open, save };
</script> <!-- далее стили -->
<style> /* ... */
</style>

Кроме того, обычно мои компоненты имеют «бюджет» в 200 строк кода вместе со стилями. Если чуть больше — не страшно. Если сильно больше, я создаю для компонента папку и начинаю выносить части в отдельный файлы с помощью svelte-preprocess, начиная со стилей. При этом сборщик настроен так, что импорты не меняются.

Что где изменилось и кто это сделал ?

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

Забавный тезис, а главное опять же применимый вообще к любому фреймворку и проекту. Более того, синтаксис хуков в современном React, так полюбившийся создателю Vue, что он даже решил срочно внедрить его во Vue 3, будет иметь все те же коллизии:

import React, { useState, useCallback } from 'react'; function MyComponent() { const [ loading, setLoading ] = useState(false); const [ status, setStatus ] = useState(0); const [ pins, setPins ] = useState([]); const ReloadPins = useCallback(async () => { setLoading(true); setPins(await getAllPins()); setStatus(0); }); return (<div></div>);
}

vs

<div></div> <script> let loading = false; let status = 0; let pins = []; async function ReloadPins() { loading = true; pins = await getAllPins(); status = 0; }
</script>

Как видите, все тоже самое, только писанины в 2 раза больше.

Причем даже не понятно меняется состояние или это просто вспомогательная переменная. Шучу, конечно же ничего не ясно. В React это решается state, который хоть как-то вносит ясности.

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

Например, в том же React, если сделать:

this.setState({ foo: 1 })

и при этом foo НЕ используется в шаблоне, механизм VDOM никак не сможет понять этого и произведет rerender/reconcile. Вы знали об этом? Поэтому, наверное, в React важно понимать, что используется в шаблонах, а что нет. В Svelte мы избавлены от подобных волнений.

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

Привязываем Stitch

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

Для чего Вам пригодится Svelte ?

В этом пункте, буду более краток — для любых новых проектов, для которых достаточно React или Vue. С Angular или Ember сравнивать не имеет смысла, потому что это фреймворка другого масштаба.

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

Svelte даёт возможность поработать в еще том, старом и ламповом JS, без PropTypes, Flow и TypeScript.

Не могу не согласиться с этим утверждением. В действительности, TS не так уж и нужен в Svelte. Хотя частичная его поддержка есть с помощью препроцессоров (без тайпчека шаблонов), но в целом это скорее довесок, чем то, без чего нельзя написать хорошие компоненты.

Почему НЕ стоит использовать Svelte ?

  1. Если ваш проект уже написан на другом фреймворке и работает сносно. Не нужно бежать и переписывать его.
  2. Даже в п.1 есть окно возможностей — можно переписать некоторые «тяжелые» компоненты на Svelte. Проект станет легче, а компоненты будут работать быстрее. При этом не придется переписывать все.
  3. Если вы пишете энтерпрайз, скорее всего лучше подойдет Angular или Ember. Svelte, ровно как и React и Vue, не всегда хороший выбор для этого.
  4. Если если вам нужна first-class поддержка Typescript.
  5. Если вы разрабатываете исключительно типовые решения и привыкли собирать проекты из готовых компонентов.
  6. До сих пор более слабый тулинг, чем у более матерых товарищей. Если это критично, тогда стоит подождать, либо присоединиться к нашему сообществу и развивать тулинг вместе.)))

***

Учитывая, что я не видел автора в русскоязычном комьюнити Svelte в Телеграм @sveltejs (1К+ участников), делаю вывод, что автор и его команда приложили недостаточно усилий при освоения нового, неизведанного для себя инструмента. В целом, к сожалению, большая часть выводов из оригинальной статьи основана на ложных предпосылках и вводит читателей в заблуждение. В данный момент кажется ребята сделали довольно поспешные выводы, впрочем это их личное дело. Думаю если бы ребята просто добавились в чатик, большей части проблем удалось бы избежать.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»