3 года назад Рич Харрис создал фреймворк под названием Svelte. Наибольшее влияние на этот новый фреймворк оказало то, что он не работает с Virtual DOM, как ReactJS, VueJS или AngularJS.
Я создал точно такое же приложение списка дел как в Svelte, так и в React. Я имею в виду не только его функциональность или визуальность, но также я не использовал сторонние библиотеки и использовал их внутренние API-интерфейсы для управления рабочим приложением.
В нашем приложении todo будет список, добавление и удаление элементов. Каждый элемент будет храниться в глобальном состоянии. Также будет компонент, который будет подписываться на глобальное хранилище и будет показывать текущее значение при его изменении.
Исходные коды Svelte и React находятся на github.
SvelteJS
$ npx degit sveltejs/template svelte-todo-list
Краткая информация о Svelte
- Работает с DOM, без Virtual DOM
- Файлы Svelte имеют расширение .svelte
- Каждый ‹style› ‹/style›, написанный в компактных файлах, имеет область видимости css.
- Вы можете использовать его API внутреннего хранилища. Нет необходимости устанавливать внешнюю библиотеку
- Сборка накопительным пакетом с конфигурацией по умолчанию. Однако вы также можете использовать Parsel и Webpack.
- Вы можете легко подписаться на любую переменную, но у нее есть несколько хитрых способов обнаружения мутаций массива. (Тот, который мне не понравился)
- Вы можете писать и javascript, и html, и стили в svelte-файлах, как если бы вы пишете html-файл. (есть небольшая разница)
- Вы получаете доступ к событиям DOM с префиксом on:, например, ‹button on: click = {} /›
- Вам не нужно передавать обратные вызовы от ребенка родителям. Вы можете использовать createEventDispatcher.
- Вы начинаете блок с {#} и заканчиваете {/}, например {#if}… {/ if}
ReactJS
$ npx create-react-app react-todo-list
Краткая информация о ReactJS
- Работает с Virtual DOM
- Вы можете использовать внутреннее глобальное управление состоянием React, которое называется Context API.
- Вы пишете исключительно javascript, у вас нет синтаксиса блочных элементов, специально предназначенного для React, как в Svelte (#if, #each…)
- События DOM - это просто события javascript, поэтому вы можете использовать onClick, как это написано на чистом javascript (стандартное событие DOM для щелчка - это onclick! Однако ваш OnCLICK oNCLiCk также будет отображаться браузером.)
- Расширение файла .js.
Некоторые сравнения кода
Давайте сравним некоторые ключевые моменты между Svelte и React.
App.svelte
<script> import TodoHeader from "./TodoHeader.svelte"; import TodoList from "./TodoList.svelte"; import { itemStore } from "./store"; </script> <style> main { font-family: sans-serif; text-align: center; } </style> <main> <div>Total item: {$itemStore.length}</div> <TodoHeader /> <TodoList /> </main>
App.js
import React from 'react'; import './App.css'; import { TodoHeader } from './TodoHeader'; import { TodoList } from './TodoList'; import { TodoListProvider } from './store'; import { Total } from './Total'; function App() { return ( <TodoListProvider todoList={[]}> <Total /> <TodoHeader /> <TodoList /> </TodoListProvider> ); } export default App;
Файлы приложений являются точками входа в обе платформы. Между двумя нет большой разницы, у них обоих есть компоненты для рендеринга. Единственное отличие здесь в привязке к глобальному стилю. В React компонент-оболочка для React.ContextAPI является родительским для всех дочерних элементов, тогда как в Svelte оболочка для глобального состояния не требуется.
svelte / store.js
import { writable } from "svelte/store"; const createItemStore = () => { const { subscribe, update } = writable([]); return { subscribe, addItem: newItem => update(items => { if (!items.find(item => item === newItem)) { items.push(newItem); } return items; }), removeItem: removedItem => update(items => { const newItems = items.filter(item => item !== removedItem); return newItems; }) }; }; export const itemStore = createItemStore();
реагировать / store.js
import React, { useState } from 'react'; export const TodoListContext = React.createContext({}); export const TodoListProvider = ({ todoList, children }) => { const [todos, setTodos] = useState(todoList); const addItem = (item) => { if (!todos || !todos.find(listItem => listItem === item)) { setTodos([...todos, item]) } }; const removeItem = (item) => { setTodos( todos.filter(listItem => listItem !== item) ) }; return ( <TodoListContext.Provider value={{ todoList: todos, addItem, removeItem }} > {children} </TodoListContext.Provider> ) } export const TodoListConsumer = TodoListContext.Consumer;
Эти два магазина делают одно и то же. У них есть оба метода действия для изменения данных хранилища. Однако в React использование Context API и создание такой же структуры, как в Svelte, может быть болезненным. Как показано в примере выше, сначала мы создали контекст из createContext, и в нашем Provider нам пришлось использовать хуки для локального хранения значений. В Svelte все проще, вы просто создаете записываемое хранилище, инициализируете его значение и возвращаете свое значение с его действиями.
svelte / TodoList
<script> import { itemStore } from "./store"; const handleRemoveItem = item => { itemStore.removeItem(item); }; </script> <style> ul { list-style-type: none; } </style> <ul> {#each $itemStore as item} <li> {item} <button on:click={() => handleRemoveItem(item)}>Remote Item</button> </li> {/each} {#if $itemStore.length === 0} <div>There is not any item added. Please add one</div> {/if} </ul>
реагировать / TodoList
import React, { useContext } from 'react'; import { TodoListContext } from './store'; export const TodoList = () => { const todoListContext = useContext(TodoListContext); const handleRemove = (item) => { todoListContext.removeItem(item); } const { todoList } = todoListContext; return ( <ul> {todoList && todoList.map(todoList => { return ( <li> {todoList} <button onClick={handleRemove.bind(null, todoList)}>Remote Item</button> </li> ) })} {(todoList.length === 0) && (<div>There is not any item added. Please add one</div>)} </ul> ) }
Что заставило меня написать свое первое приложение Svelte, так это использование условий и циклов в компоненте. Использование синтаксиса другого, а не чистого javascript для циклов или условий - это то, что мне не нравится. Но доступ к элементу магазина с помощью только импорта и отсутствие необходимости в каком-либо внешнем хуке или уровне для доступа к глобальному хранилищу - это ключевое различие между Svelte и React.
svelte / TodoHeader
<script> import { onMount } from "svelte"; import { itemStore } from "./store"; let value; onMount(() => { value = ""; }); const handleAddItem = () => { itemStore.addItem(value); value = ""; }; </script> <style> .disabled { color: graytext; } </style> <input type="text" bind:value placeholder="Item name" /> <button on:click={handleAddItem} disabled={!value} class:disabled={!value}> Add Item </button>
реагировать / TodoHeader
import React, { useContext, useState } from 'react'; import { TodoListContext } from './store'; export const TodoHeader = () => { const todoListStore = useContext(TodoListContext); const [itemName, setItemName] = useState('') const handleAddItem = (e) => { e.preventDefault(); todoListStore.addItem(itemName); setItemName(''); } return ( <> <input placeholder="Item name" type="text" value={itemName} onChange={(event) => setItemName(event.target.value.trim())} /> <button onClick={handleAddItem} disabled={!itemName} className={!itemName && 'disabled'}>Add Item</button> </> ) }
В Svelte вам не нужно сохранять значение ввода во внешнем состоянии. Svelte упрощает привязку ключевого слова к любому существующему свойству, с дополнительной функцией, которая позволяет отслеживать изменения этой опоры с помощью реактивного объявления без дополнительных усилий для значений (не для массивов).
Полученные результаты
Кроме того, давайте поделимся некоторыми результатами о процессе сборки, трудностях и удобствах, с которыми я столкнулся во время разработки.
Я следовал точно такому же образцу при разработке обоих. Я не использовал никаких внешних библиотек и использовал только внутренний API, поддерживаемый обеими фреймворками. Есть компоненты, связанные с глобальным состоянием. Есть компоненты, подписанные на глобальные переменные состояния.
Что мне нравится в Svelte ❤️
- Строительство действительно быстрое. Но это может быть также из-за использования Rollup в качестве сборщика, я думаю, было бы неплохо протестировать сборку также с Webpack.
- Опять же, размер пакета действительно мал по сравнению с React. Однако это также может быть связано с производительностью Rollup «встряхивая деревья».
- Использование магазина действительно гибкое. Вы можете адаптировать свои компоненты в магазине и легко начать подписываться или изменять значения магазина. ❤️
- Мне понравилось, что все стили, написанные в компоненте, имеют область видимости css.
- Связывание условного стиля имеет простой синтаксис, который не требует создания логики для связывания классов. (класс: отключено = переменная)
Что мне не нравится в Svelte 💔
- Мне очень не нравится писать шаблоны в файл, например #each, #if. Я не предпочитаю использовать новые подходы, для которых я могу делать это исключительно с помощью JavaScript.
- Когда вы используете реактивные объявления для подписки на изменение переменной, Svelte не может обнаруживать мутации в массивах. Svelte предлагает несколько хитростей для этой ситуации.
- Некоторым разработчикам может понравиться написание шаблонов в компонентах, но это все еще то, что мне не нравится. Кроме того, этот подход предполагает использование шаблонов для циклов, условий и т. Д., О которых я упоминал в пункте 1.
- Вы должны указать уникальный идентификатор для каждого элемента в списке, иначе всякий раз, когда вы захотите выполнить действие с элементом в списке, может быть выполнен неправильный элемент.
- Мне не нравится стиль использования в событиях DOM, таких как on: click. Я предпочитаю использовать onClick как обычное событие DOM. (стандартным событием DOM для щелчка является onclick! однако ваш OnCLICK oNCLiCk также будет отображаться браузером.)
Сравнение производственной сборки
SvelteJS использует Rollup и ReactJS Webpack по умолчанию, и я не касался их конфигураций, они имеют конфигурацию по умолчанию. Время сборки Svelte составляло 1,40 с, а React - 5,49 с. Здесь Svelte побеждает! ❤️
Оба приложения являются серверными, обслуживая пакет npm. Svelte экспортирует свои пакеты в общую папку, а приложение React помещает их в папку сборки.
serve -s build // this is for react app serve -s public // this is for svelte app
Когда я проверил вкладку сети, пакет Svelte занимает всего 2,9 КБ после gzip, тогда как пакет React составляет всего 42,1 КБ после gzip. и здесь Svelte выигрывает своими размерами комплектации на продакшене ❤️
Если вам нравятся мои статьи, вы можете поддержать меня, хлопая в ладоши и подписываясь на меня.
Я тоже на linkedin, все приглашения приветствуются.