Автор Клара Экекента✏️
Выбор фронтенд-фреймворка может быть трудным решением для разработчика, потому что существует так много вариантов. React — один из самых популярных вариантов. Он хорошо зарекомендовал себя и имеет рейтинг удовлетворенности 84% по данным опроса 2021 года State of JS Survey. Тем не менее, существует несколько других фреймворков с интересными особенностями и функциональностью, которые стоит изучить.
При выборе фронтенд-фреймворка для своего следующего проекта обратите внимание на следующие вопросы:
- Обладает ли этот фреймворк теми функциями, которые мне нужны?
- Насколько быстро работает этот фреймворк по сравнению с другими?
- Насколько прост этот фреймворк в освоении и использовании?
- Какого размера сообщество использует этот фреймворк?
Одной из альтернатив React является Lit, который имеет рейтинг удовлетворенности 77% по данным исследования 2021 State of JS Survey. Lit прост в освоении и использовании, а его небольшая площадь позволяет быстро загружать файлы.
В этом учебнике мы сравним React и Lit. Мы также создадим пример проекта на Lit.
Перейти вперед:
- Что нового в Lit?
- Lit против React
- JSX и шаблонизация
- Компоненты и реквизиты
- Состояние и методы жизненного цикла
- Хуки
- Ссылки
- Создание базового проекта в Lit
- Стоит ли мне переходить с React на Lit?
Давайте начнем!
- Что нового в Lit?
- Lit против React
- JSX и шаблонизация
- Компоненты и реквизиты
- Компоненты класса
- Функциональные компоненты
- Состояние и методы жизненного цикла
- constructor
- render
- componentDidMount против firstUpdated и connectedCallback
- componentDidUpdate vs. updated
- componentWillUnmount vs.disconnectedCallback
- Крючки
- Refs
- Создание базового проекта дел в Lit
- Стоит ли мне переходить с React на Lit?
- Полная видимость производственных приложений React
- Полная видимость производственных приложений React
Что нового в Lit?
Lit имеет несколько особенностей, которые отличают его от других фронтенд-фреймворков:
- Базовый класс LitElement — это удобное и универсальное расширение родного HTMLElement. Этот класс может быть расширен для определения наших компонентов
- Выразительные и декларативные шаблоны позволяют легко определить, как компонент должен быть отображен.
- Реактивные свойства — это внутреннее состояние компонентов Lit. Компоненты автоматически перерисовываются при изменении реактивного свойства
- Масштабируемые стили помогают сохранить простоту селекторов CSS, гарантируя, что стили компонентов не влияют на другие контексты.
- Поддерживает Vanilla Javascript, TypeScript и эргономику (декораторы и объявления типов).
Lit против React
Основные концепции и возможности Lit во многом схожи с концепциями и возможностями React, но есть и существенные различия. Например, React существует с 2013 года и гораздо более популярен, чем Lit. На момент написания статьи React имеет около 15,9 миллионов еженедельных загрузок на npm по сравнению с 127 тысячами еженедельных загрузок на npm для Lit.
Однако Lit быстрее React и занимает меньше памяти. Публичное сравнение бенчмарков показало, что lit-html на 8-10 процентов быстрее, чем VDOM от React. Объем памяти Lit составляет 5 кБ, по сравнению с 40 кБ для React.
Эти два фреймворка предлагают и другие интересные возможности. Давайте посмотрим, как они сравниваются.
JSX и шаблонизация
JSX — это синтаксическое расширение JavaScript, которое функционирует аналогично языку шаблонов, но с использованием всех возможностей JavaScript. Пользователи React могут использовать JSX, чтобы легко писать шаблоны в коде JavaScript. Лит-шаблоны служат аналогичной цели, но выражают пользовательский интерфейс компонента как функцию его состояния.
Вот пример шаблонизации JSX в React:
import 'react';
import ReactDOM from 'react-dom';
const name = 'World';
const el = (
<>
<h1>Hello, {name}</h1>
<div>How are you? </div>
</>
);
ReactDOM.render(
el,
mountNode
);
А вот пример шаблонизации в Lit:
import {html, render} from 'lit';
const name = 'World';
const el = html`
<h1>Hello, ${name}</h1>
<div>How are you?</div>`;
render(
el,
mountNode
);
Как видно из приведенных примеров, Lit не нужен фрагмент React для группировки нескольких элементов в своих шаблонах. Вместо этого шаблоны Lit обернуты HTML-тегированным литералом шаблона.
Компоненты и реквизиты
Компоненты — это самодостаточные, многократно используемые фрагменты кода. Они выполняют те же действия, что и функции JavaScript, но работают независимо и возвращают HTML. Компоненты React делятся на два типа: компоненты классов и функциональные компоненты.
Компоненты класса
Lit-эквивалент компонента класса React называется LitElement.
Вот пример компонента на основе класса в React:
import React from 'react';
import ReactDOM from 'react-dom';
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {name: ''};
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const el = <Welcome name="World"/>
ReactDOM.render(
el,
mountNode
);
Вот тот же пример в Lit с использованием LitElement
:
import {LitElement, html} from 'lit';
class WelcomeBanner extends LitElement {
static get properties() {
return {
name: {type: String}
}
}
constructor() {
super();
this.name = '';
}
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
}
customElements.define('welcome-banner', WelcomeBanner);
После определения и рендеринга шаблона для компонента LitElement мы добавляем следующее в наш HTML-файл:
<!-- index.html -->
<head>
<script type="module" src="./index.js"></script>
</head>
<body>
<welcome-banner name="World"></welcome-banner>
</body>
Теперь давайте рассмотрим, как создаются функциональные компоненты в этих фреймворках.
Функциональные компоненты
Lit не использует JSX, поэтому нет корреляции один к одному с функциональным компонентом React. Однако проще написать функцию, которая принимает свойства и затем рендерит DOM на основе этих свойств.
Вот пример функционального компонента в React:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const el = <Welcome name="World"/>
ReactDOM.render(
el,
mountNode
);
Вот тот же пример в Lit:
import {html, render} from 'lit';
function Welcome(props) {
return html`<h1>Hello, ${props.name}</h1>`;
}
render(
Welcome({name: 'World}),
document.body.querySelector('#root')
);
Состояние и методы жизненного цикла
state
— это объект React, который содержит данные или информацию о компоненте. Состояние state
компонента может изменяться с течением времени. Каждый раз, когда его state
изменяется, компонент перерисовывается.
Реактивные свойства Lit — это смесь state
и props
от React. При изменении реактивные свойства могут запустить жизненный цикл компонента, повторно отображая компонент и, по желанию, считывая или записывая атрибуты. Реактивные свойства бывают двух видов:
- Публичные реактивные свойства
- Внутреннее реактивное состояние
Реактивные свойства реализуются в React следующим образом:
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props)
this.state = {name: 'there'}
}
componentWillReceiveProps(nextProps) {
if (this.props.name !== nextProps.name) {
this.setState({name: nextProps.name})
}
}
}
Реактивные проепринты реализованы в Lit, например, так:
import {LitElement} from 'lit';
import {property} from 'lit/decorators.js';
class MyEl extends LitElement {
@property() name = 'there';
}
Внутреннее реактивное состояние относится к реактивным свойствам, которые не отображаются в публичном API компонента. Эти свойства состояния не имеют соответствующих атрибутов и не предназначены для использования за пределами компонента. Внутреннее реактивное состояние компонента должно определяться самим компонентом.
React и Lit имеют схожий жизненный цикл, с некоторыми небольшими, но заметными различиями. Давайте подробнее рассмотрим некоторые общие для этих фреймворков методы.
constructor
Метод constructor
доступен как в React, так и в Lit. Он автоматически вызывается при создании объекта из класса
.
Вот пример метода constructor
в React:
import React from 'react';
import Chart from 'chart.js';
class MyEl extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this._privateProp = 'private';
}
Вот пример метода constructor
в Lit:
class MyEl extends LitElement {
static get properties() {
return { counter: {type: Number} }
}
constructor() {
this.counter = 0;
this._privateProp = 'private';
}
render
Метод render
доступен как в React, так и в Lit. Он отображает код внутри указанного элемента.
Вот пример метода render
в React:
render() {
return <div>Hello World</div>
}
Вот пример метода render
в Lit:
render() {
return html`<div>Hello World</div>`;
}
componentDidMount
против firstUpdated
и connectedCallback
Функция componentDidMount
в React похожа на комбинацию обратных вызовов жизненного цикла Lit’s firstUpdated
и connectedCallback
. Эта функция вызывается после подключения компонента.
Вот пример метода componentDidMount
в React:
componentDidMount() {
this._chart = new Chart(this.chartElRef.current, {...});
}
componentDidMount() {
this.window.addEventListener('resize', this.boundOnResize);
}
Вот пример обратных вызовов жизненного цикла firstUpdated
и connectedCallback
в Lit:
firstUpdated() {
this._chart = new Chart(this.chartEl, {...});
}
connectedCallback() {
super.connectedCallback();
this.window.addEventListener('resize', this.boundOnResize);
}
componentDidUpdate
vs. updated
Функция componentDidUpdate
в React эквивалентна updated
в Lit. Она вызывается после изменения реквизитов или состояния компонента.
Вот пример метода componentDidUpdate
в React:
componentDidUpdate(prevProps) {
if (this.props.title !== prevProps.title) {
this._chart.setTitle(this.props.title);
}
}
Вот пример метода updated
в Lit:
updated(prevProps: PropertyValues<this>) {
if (prevProps.has('title')) {
this._chart.setTitle(this.title);
}
}
componentWillUnmount
vs.disconnectedCallback
Функция componentWillUnmount
в React эквивалентна disconnectedCallback
в Lit. Эта функция вызывается после уничтожения или размонтирования компонента.
Вот пример метода componentWillUnmount
в React:
componentWillUnmount() {
this.window.removeEventListener('resize', this.boundOnResize);
}
}
Вот пример метода disconnectedCallback
в Lit:
disconnectedCallback() {
super.disconnectedCallback();
this.window.removeEventListener('resize', this.boundOnResize);
}
}
Крючки
Хуки — это функции, которые позволяют функциональным компонентам React «подключаться» к состоянию React и функциям жизненного цикла. Хуки не работают внутри классов, но они позволяют нам использовать React без классов.
В отличие от React, Lit не предлагает способ создания пользовательских элементов из функции, но LitElement решает большинство основных проблем с компонентами классов React:
- Не принимает аргументы в конструкторе
- Автоматическая привязка всех @event привязок (как правило, к ссылке на пользовательский элемент)
- Инстанцирование свойств класса в качестве членов класса
Вот пример хуков в React (на момент создания хуков):
import React from 'react';
import ReactDOM from 'react-dom';
class MyEl extends React.Component {
constructor(props) {
super(props); // Leaky implementation
this.state = {count: 0};
this._chart = null; // Deemed messy
}
render() {
return (
<>
<div>Num times clicked {count}</div>
<button onClick={this.clickCallback}>click me</button>
</>
);
}
clickCallback() {
// Errors because `this` no longer refers to the component
this.setState({count: this.count + 1});
}
}
Вот тот же пример с использованием LitElement:
class MyEl extends LitElement {
@property({type: Number}) count = 0; // No need for constructor to set state
private _chart = null; // Public class fields introduced to JS in 2019
render() {
return html`
<div>Num times clicked ${count}</div>
<button @click=${this.clickCallback}>click me</button>`;
}
private clickCallback() {
// No error because `this` refers to component
this.count++;
}
}
Refs
Refs — это функции React, которые позволяют нам получить доступ к элементу DOM и любым элементам React, которые мы создали. Они используются, когда мы хотим изменить значение дочернего компонента без использования props.
В Lit рефссылки создаются с помощью декораторов @query
и @queryAll
. Эти декораторы почти эквивалентны querySelector
и querySelectorAll
, соответственно, и выводятся непосредственно в DOM.
Вот пример функции refs в React:
const RefsExample = (props) => {
const inputRef = React.useRef(null);
const onButtonClick = React.useCallback(() => {
inputRef.current?.focus();
}, [inputRef]);
return (
<div>
<input type={"text"} ref={inputRef} />
<br />
<button onClick={onButtonClick}>
Click to focus on the input above!
</button>
</div>
);
};
Вот тот же пример в Lit с использованием декоратора @query
:
@customElement("my-element")
export class MyElement extends LitElement {
@query('input') // Define the query
inputEl!: HTMLInputElement; // Declare the prop
// Declare the click event listener
onButtonClick() {
// Use the query to focus
this.inputEl.focus();
}
render() {
return html`
<input type="text">
<br />
<!-- Bind the click listener -->
<button @click=${this.onButtonClick}>
Click to focus on the input above!
</button>
`;
}
}
Создание базового проекта дел в Lit
Давайте посмотрим на Lit в действии, создав пример проекта дел.
Для начала выполните команду для клонирования стартового JavaScript-проекта Lit:
git clone https://github.com/lit/lit-element-starter-js.git
Затем перейдите в папку проекта и установите необходимые пакеты с помощью этой команды:
npm install
После завершения установки перейдите к файлу lit-element-starter-js/my-element.js
. Удалите коды boilerplates и создайте компонент Todo
со следующим фрагментом кода:
import {LitElement, html, css} from 'lit';
class Todo extends LitElement {
constructor() {
super();
}
render() {
return html`
<div class="todos-wrapper">
<h4>My Todos List</h4>
<input placeholder="Add task..."/>
<button>Add</button>
<div class="list">
#Todo List
</div>
</div>
`;
}
}
customElements.define('my-element', Todo);
Приведенный выше код создает компонент Todo
с методом constructor
, где будут определены все реактивные свойства приложения, и методом render
, который рендерит JSX, содержащий поле ввода и кнопку.
Далее определим свойства приложения. Поскольку это приложение для выполнения дел, нам понадобится TodosList
для хранения задач и свойство input
для получения пользовательского ввода.
Теперь добавим приведенный ниже фрагмент кода в класс Todos
:
static properties = {
TodosList: {type: Array},
input: {type: String},
};
Затем с помощью приведенного ниже кода присвоим начальные значения свойствам TodosList
и input
в методе constructor
:
this.TodosList = [];
this.input = null;
Далее мы создадим метод для добавления и обновления задач:
setInput(event) {
this.input = event.target.value;
}
addTodo() {
this.TodosList.push({
name: this.input,
id: this.TodosList.length + 1,
completed: false,
});
this.requestUpdate();
}
updateTodo(todo) {
todo.completed = !todo.completed;
this.requestUpdate();
}
В приведенном выше коде видно, что функция requestUpdate()
была вызвана в методах addTodo
и updateTodo
после изменения состояния. Эти методы изменяли свойство TodosList
, поэтому мы вызвали функцию requestUpdate()
для обновления состояния компонента.
Далее мы изменим метод render
, чтобы добавить слушателей событий к методам, созданным выше, и отобразить задачи дел.
render() {
return html`
<div class="todos-wrapper">
<h4>My Todos List</h4>
<input placeholder="Add task..." @input=${this.setInput} />
<button @click=${this.addTodo}>Add</button>
<div class="list">
${this.TodosList.map(
(todo) => html`
<li
@click=${() => this.updateTodo(todo)}
class=${todo.completed && 'completed'}
>
${todo.name}
</li>
`
)}
</div>
</div>
`;
}
Наконец, добавим некоторые стилизации, чтобы приложение выглядело более привлекательно:
static styles = css`
.todos-wrapper {
width: 35%;
margin: 0px auto;
background-color: rgb(236, 239, 241);
padding: 20px;
}
.list {
margin-top: 9px;
}
.list li {
background-color: white;
list-style: none;
padding: 6px;
margin-top: 3px;
}
.completed {
text-decoration-line: line-through;
color: #777;
}
input {
padding: 5px;
width: 70%;
}
button {
padding: 5px;
}
`;
Теперь давайте запустим приложение:
npm run serve
Вот наш пример проекта!
Стоит ли мне переходить с React на Lit?
У каждого фреймворка есть свои сильные и слабые стороны. На React работают веб-приложения многих крупных компаний, таких как Facebook, Twitter и Airbnb. Кроме того, у него обширное сообщество разработчиков и соавторов.
Если вы сейчас используете React и довольны этим выбором, то я не вижу причин для перехода. Однако если вы работаете над проектом, требующим действительно высокой производительности, то вам стоит рассмотреть возможность использования Lit.
Чтобы узнать больше о Lit, ознакомьтесь с его официальной документацией.
Полная видимость производственных приложений React
Отладка приложений React может быть сложной задачей, особенно когда пользователи сталкиваются с проблемами, которые трудно воспроизвести. Если вы заинтересованы в мониторинге и отслеживании состояния Redux, автоматическом обнаружении ошибок JavaScript, отслеживании медленных сетевых запросов и времени загрузки компонентов, попробуйте LogRocket.
LogRocket — это как видеорегистратор для веб- и мобильных приложений, записывающий буквально все, что происходит в вашем React-приложении. Вместо того чтобы гадать, почему возникают проблемы, вы можете собрать данные и отчитаться о том, в каком состоянии находилось ваше приложение в момент возникновения проблемы. LogRocket также отслеживает производительность вашего приложения, предоставляя такие показатели, как загрузка процессора клиента, использование памяти клиента и многое другое.
Пакет промежуточного ПО LogRocket Redux добавляет дополнительный уровень видимости пользовательских сессий. LogRocket регистрирует все действия и состояние ваших хранилищ Redux.
Модернизируйте способы отладки приложений React — начните мониторинг бесплатно.
Полная видимость производственных приложений React
Отладка приложений React может быть сложной задачей, особенно когда пользователи сталкиваются с проблемами, которые трудно воспроизвести. Если вы заинтересованы в мониторинге и отслеживании состояния Redux, автоматическом выявлении ошибок JavaScript, отслеживании медленных сетевых запросов и времени загрузки компонентов, попробуйте LogRocket.
LogRocket — это как видеорегистратор для веб- и мобильных приложений, записывающий буквально все, что происходит в вашем React-приложении. Вместо того чтобы гадать, почему возникают проблемы, вы можете собрать данные и отчитаться о том, в каком состоянии находилось ваше приложение в момент возникновения проблемы. LogRocket также отслеживает производительность вашего приложения, предоставляя такие показатели, как загрузка процессора клиента, использование памяти клиента и многое другое.
Пакет промежуточного ПО LogRocket Redux добавляет дополнительный уровень видимости пользовательских сессий. LogRocket регистрирует все действия и состояние ваших хранилищ Redux.
Модернизируйте отладку приложений React — начните мониторинг бесплатно.