Как стать автором
Обновить
2765.06
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Когда исчезнут JavaScript-фреймворки?

Время на прочтение11 мин
Количество просмотров44K
Автор оригинала: Luke Joliat
Автор материала, перевод которого мы сегодня публикуем, веб-разработчик, говорит, что он старается регулярно пересматривать набор инструментов, которыми пользуется. Делает он это для того, чтобы понять, может ли он без некоторых из них обойтись, решая свои обычные задачи. Недавно он решил провести эксперимент и создать сложное фронтенд-приложение без использования JavaScript-фреймворков.

Что такое «фреймворк»?


Если попытаться дать определение JavaScript-фреймворка, не вдаваясь в детали, то окажется, что это — инструмент, который можно использовать для разработки сложных веб-приложений, в частности — одностраничных приложений (Single Page Application, SPA).

В былые времена такие приложения создавали, полагаясь на возможности чистого JavaScript и библиотеки jQuery. Но, с ростом сложности фронтенд-приложений, начали появляться и соответствующие инструменты, облегчающие жизнь программистов. Например — это React, Angular и Vue.

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

  • Синхронизация состояния и визуального представления приложения.
  • Маршрутизация.
  • Система шаблонов.
  • Компоненты, подходящие для повторного использования.

Необходимы ли фреймворки современному разработчику?


Ответ на вопрос, вынесенный в заголовок этого раздела, зависит от того, как относиться к идее «необходимости» фреймворков. Уверен, многие могут сказать о том, что фронтенд-фреймворки не являются необходимыми в наборе инструментов веб-разработчика, и никогда необходимыми не были. Хотя бесспорно то, что это — весьма полезные инструменты.

Собственно говоря, наш вопрос можно переформулировать так: «Являются ли фреймворки чем-то вроде «современной библиотеки jQuery»? Можно ли решать задачи, на решение которых они направлены, другими средствами, например такими, которые появились в распоряжении программистов в ходе развития браузерных API»?


jQuery

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

Для того чтобы исследовать эту идею, я разработал одностраничное приложение, используя только JavaScript, стандартные веб-компоненты и бандлер Parcel. В процессе работы я столкнулся с некоторыми проблемами и сложностями, глядя на которые особенно чётко начинаешь видеть сильные стороны современных JS-фреймворков.

В то же время, как только я справился с первоначальными препятствиями, я удивился тому, как легко создать одностраничное приложение на чистом JavaScript.

Обзор экспериментального приложения


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


Домашняя страница приложения


Страница создания записи о рецепте

Веб-компоненты


Создание веб-компонентов — занятие несложное. В частности, речь идёт о создании класса, расширяющего HTMLElement (или HTMLParagraphElement, и так далее) и о последующем использовании этого класса для описания собственного элемента.

Кроме того, при работе с веб-компонентами можно использовать хуки их жизненного цикла, такие, как connectedCallback, disconnectedCallback, attributeChangedCallback.

Вот код компонента recipe-item, который предназначен для вывода рецептов в списке.

import template from './recipe.html'
import DATA_SERVICE from '../../utils/data'
export default class Recipe extends HTMLElement {
  constructor () {
    // подключаем теневую DOM, инициализируем приватное свойство recipe и DATA_SERVICE()
    super()
    this._shadowRoot = this.attachShadow({ mode: 'open' })
    this._recipe = null
    this.ds = new DATA_SERVICE()
  }
  connectedCallback () {
    // устанавливаем в качестве html-содержимого импортированный шаблон
    this._shadowRoot.innerHTML = template
    // прикрепляем метод delete к соответствующей кнопке
    this._shadowRoot
      .querySelector('.delete')
      .addEventListener('click', () => this._delete())
  }
  _render (title) {
    // задаём заголовок рецепта и текст кнопки, позволяющей отметить рецепт
    this._shadowRoot.querySelector('.recipe-title').innerHTML = title
    this._shadowRoot.querySelector('.favorite').innerHTML = this._recipe
      .favorite
      ? 'Unfavorite'
      : 'Favorite'
  }
  _delete () {
    // удаление рецепта или вывод сообщения об ошибке
    try {
      await this.ds.deleteRecipe(this._recipe.id)
    } catch (e) {
      console.error(e)
      alert(
        'Sorry, there was a problem deleting the recipe. Please, try again.'
      )
    }
  }
  get recipe () {
    // геттер для рецепта
    return this._recipe
  }
  set recipe (recipe = {}) {
    // сеттер для рецепта, вызывающий метод render
    this._recipe = recipe
    this._render(this._recipe.title)
  }
}

window.customElements.define('recipe-item', Recipe)

Маршрутизация


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

Изначально я, для организации навигации в приложении, использовал npm-пакет Vanilla JS Router. Но, с использованием API History, совсем несложно реализовать собственный маршрутизатор, на который придётся что-то около 100 строк кода. Обратите внимание на то, что в этом примере не реализовано что-то действительно сложное, наподобие средств ограничения доступа к определённым маршрутам (route guard).

import './components/error/error'
import content404 from './components/404/404.html'
import DATA_SERVICE from './utils/data'
const ds = new DATA_SERVICE()
// получаем элемент, содержащий SPA
const $el = document.getElementById('app')

// объявляем маршруты
const home = async () => {
  await import('./components/recipe/recipe')
  await import('./components/recipe-list/recipe-list')
  await import('./components/modal/modal.js')
  $el.innerHTML = `<recipe-list></recipe-list>`
}

const create = async () => {
  await import('./components/create-recipe/create-recipe')
  $el.innerHTML = `<create-recipe></create-recipe>`
}

const edit = async () => {
  await import('./components/edit-recipe/edit-recipe')
  $el.innerHTML = `<edit-recipe></edit-recipe>`
}

const error404 = async () => {
  $el.innerHTML = content404
}

// установление соответствия маршрутов и путей
// получение рецепта по id для создания маршрута редактирования рецепта
const routes = {
  '/': home,
  '/create': create,
  '/error': error404,
  '/edit': async function (params) {
    const id = params.get('id')
    const recipe = await ds.getRecipe(id)
    await edit()
    $el.querySelector('edit-recipe').recipe = recipe
  }
}

// по событию onpopstate получить параметры из URL и передать их маршруту
// если нужного маршрута найти не удаётся - использовать маршрут /error
window.onpopstate = async () => {
  const url = new URL(
    window.location.pathname + window.location.search,
    window.location.origin
  )
  if (routes[window.location.pathname]) {
    await routes[window.location.pathname](url.searchParams)
  } else routes['/error']()
}
// добавление маршрута в историю браузера
let onNavItemClick = async pathName => {
  const url = new URL(pathName, window.location.origin)
  const params = url.searchParams
  if (routes[url.pathname]) {
    window.history.pushState({}, pathName, window.location.origin + pathName)
    await routes[url.pathname](params)
  } else {
    window.history.pushState({}, '404', window.location.origin + '/404')
    routes['/error']()
  }
}

// установка подходящего маршрута при загрузке и перезагрузке страницы
;(async () => {
  const url = new URL(
    window.location.pathname + window.location.search,
    window.location.origin
  )
  if (routes[window.location.pathname]) {
    await routes[window.location.pathname](url.searchParams)
  } else routes['/error']()
})()

// экспорт маршрутов и метода onNavItemClick()
const router = {
  onNavItemClick,
  routes
}
export { router }

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

Сравнение разработки с использованием JS и фреймворков


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

Минусы отказа от фреймворков


▍Проблемы стандартизации


Спецификации веб-компонентов, с одной стороны, существуют уже довольно давно, а с другой, всё ещё развиваются. Так, веб-компоненты были представлены Алексом Расселом на мероприятии Fronteers Conference 2011. Однако серьёзная работа в направлении их поддержки и развития ведётся только в последние год или два. В результате в спецификациях всё ещё царит беспорядок. Например, технология HTML-импортов уже неактуальна, хотя о ней пишут в документации и в различных публикациях.

▍Тестирование


Имеется не особенно много решений для тестирования стандартных веб-компонентов. Так, существуют некоторые многообещающие инструменты — вроде skatejs ssr или web-component-tester. Но эти инструменты предназначены для применения с соответствующими библиотеками. В результате у того, кто хочет пользоваться веб-компонентами, возникают определённые сложности.

▍Синхронизация интерфейса и состояния приложения



Конструкции querySelector()

При разработке веб-приложений очень важно, чтобы в распоряжении программиста была бы некая базовая система, автоматически синхронизирующая данные и их представление на странице. Именно такие системы и привлекают многих к Angular и к другим фреймворкам.

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

▍Технология Shadow DOM


Я всё ещё сомневаюсь в технологии Shadow DOM. С одной стороны мне нравится идея инкапсуляции. Это — адекватный паттерн проектирования, упрощающий управление стилями и облегчающий работу программиста. Но, с другой стороны, его использование сопряжено со сложностями, в том случае, если нужно, чтобы что-то выходило бы за границы инкапсулированных сущностей. Например, это касается применения общих стилей. Вопрос о том, как их использовать правильно, всё ещё открыт.

▍Работа с DOM


Фреймворки и библиотеки вроде Angular и React отчасти обязаны своей притягательностью тем, что они значительно упрощают работу с DOM. А именно, они отлично показывают себя при выводе и обновлении элементов. Вот что говорится об этом в блоге Angular University: «Angular напрямую генерирует структуры данных DOM, а не работает, создавая HTML-код и потом передавая его браузеру для последующей обработки».

Angular, например, в отличие от jQuery, напрямую генерирует структуры данных DOM. Делается это вместо того, чтобы передавать браузеру HTML-код, который, перед попаданием в DOM, должен быть разобран. Такой подход отличается более высокой производительностью, так как благодаря его использованию устраняется шаг разбора HTML-кода. Технология Virtual DOM также весьма полезна, так как она позволяет отказаться от повторного рендеринга всего содержимого страницы, выполняемого каждый раз при обновлении неких элементов.

Минусы отказа от фреймворков в пользу обычного JS мы рассмотрели. Теперь поговорим о плюсах.

Плюсы отказа от фреймворков


▍Размеры пакетов приложений


Пакет готового веб-приложения, разработанного на чистом JS, может оказаться (обратите внимание на слово «может») гораздо меньше, чем пакет аналогичного приложения, созданного с применением фреймворков. Например, пакет нашего готового экспериментального приложения, реализующего множество возможностей, в три раза меньше чем пакет пустого Angular-приложения.


Пакет пустого Angular-приложения


Пакет приложения, разработанного без использования фреймворков

▍Использование фреймворков и понимание технологий


Если вы разрабатывали веб-приложения исключительно с использованием неких фреймворков и их CLI, для того, чтобы создать приложение без вспомогательных инструментов, вам придётся очень постараться. Создание проектов без фреймворков позволяет изучить базовые технологии, инструменты и паттерны проектирования, которые лежат в основе современной веб-разработки. Эти знания, в любом случае, будете ли вы, приобретя их, пользоваться фреймворками, или не будете, окажутся полезными.

▍Производительность


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

Тут хочется отметить, что команды разработчиков React и Angular, очевидно, знают о подобных проблемах. Поэтому они уделяют немалое внимание оптимизации своих проектов. Например, речь идёт о чём-то вроде использования в React метода shouldUpdate() или о стратегии обнаружения изменений onPush в Angular.

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


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

Некоторые заметки и интересные наблюдения


Мне очень понравилось работать с Parcel. Когда я искал выходы из сложных ситуаций, я чувствовал себя ограниченным немного сильнее, чем при работе с Webpack, но я выяснил, что, по большей части, заявление о том, что Parcel не нуждается в настройке, отражает истинное положение дел.

Ранее я был склонен считать React, Vue и Angular средствами для решения одних и тех же задач. Поэтому я называл их все «фреймворками». Однако теперь мне ясно, почему React правильнее называть «библиотекой», а Vue — «прогрессивным фреймворком».

Почему я не воспользовался Stencil или Polymer? Дело в том, что я, при работе над рассматриваемым здесь проектом, стремился к тому, чтобы не использовать какие-либо пакеты, библиотеки или фреймворки. Правда, это не относится к средствам сборки проектов. Мне хотелось на собственном опыте увидеть то, что способны предложить современному разработчику чистые веб-стандарты.

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

Базовые технологии или фреймворки?


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

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

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

Итоги


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

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

Уважаемые читатели! Разрабатывали ли вы веб-приложения, стремясь к минимальному использованию чужого кода?
Теги:
Хабы:
+51
Комментарии171

Публикации

Информация

Сайт
ruvds.com
Дата регистрации
Дата основания
Численность
11–30 человек
Местоположение
Россия
Представитель
ruvds