image


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


RESTful API


Первое, о чём я хочу рассказать, это концепция RESTful API. Термин REST, или RESTful API, всплывает во многих беседах между веб-разработчиками, и на то есть веская причина. REST (REpresentational State Transfer, передача состояния представления) API и веб-сервисы предоставляют простой способ взаимодействия с архитектурой бэкенда без необходимости разбираться в этой самой архитектуре.


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


RESTful API бывают самыми разными. Наиболее популярные из них возвращают JSON-объекты, которыми можно легко манипулировать посредством JavaScript на стороне клиента, что позволяет фронтенд-разработчикам эффективно работать с одними лишь частями View и Controller паттерна MVC (Model-View-Controller).


TL;DR: RESTful API очень популярны и предоставляют фронтенд-разработчикам возможность взаимодействовать с ресурсами в вебе, сосредоточившись на разработке фронтенда и не беспокоясь об архитектуре.


AJAX


AJAX (Asyncronous JavaScript And XML) существует уже немало лет, и каждый разработчик так или иначе использовал его в своей работе (большинство из нас — посредством jQuery). Здесь я не будут углубляться в устройство AJAX, поскольку в сети есть сотни источников получше, но хочу отнять у вас минутку времени, чтобы просто восхититься теми возможностями, которые эта технология даёт фронтенд-разработчикам.


С помощью AJAX мы можем запрашивать ресурсы из одного одного или нескольких мест (или локально, если страница расположена на том же сервере, что и запрашиваемый URI) и в любое время, без замедления работы веб-приложений или необходимости начать отрисовку всех данных. Фактически, мы можем не грузить любой контент на страницу, а затем просто запрашивать его, как только будет загружена пустая HTML-страница. В сочетании с RESTful API получается невероятно гибкое, высокопортируемое и удобное в сопровождении решение для веб-приложений.


TL;DR: AJAX — очень мощный инструмент, который в сочетании с RESTful API позволяет создавать по-настоящему динамичные веб-приложения, которые быстро загружаются и отображают контент из ресурсов в вебе.


Получение контента из веба


Поначалу создание вашей первой веб-страницы может показаться довольно трудной задачей, так что будем идти постепенно. Начнём с получения контента из RESTful API с помощью AJAX-вызовов.


Первым делом нужно найти высококачественный API, желательно такой, который возвращает JSON. Вот для примера несколько ресурсов, которые выводят placeholder-контент и пригодны для создания примера веб-приложения:


  • JSON Placeholder — placeholder-текст, выдаваемый в JSON-формате, подходящий для многих вариантов использования. Очень прост для начинающих, идеально подходит для заполнения макета (mockup) веб-приложения, вроде того, что мы с вами сделаем.
  • Unsplash It — placeholder-контент не будет полным без изображений, и именно отсюда их надо брать. Документация очень понятная и простая в применении, так что можно сразу начинать пользоваться.
  • Random User Generator? — ещё один высококачественный ресурс, предоставляющий сгенерированные пользовательские профили, которые можно сконфигурировать под свои нужды. Здесь есть куча настроек, но для целей статьи нужно совсем немного.

Мы воспользуемся этими тремя ресурсами, но вы можете прошерстить список в конце статьи в поисках более интересных API, пригодных для генерирования placeholder-контента для вашей страницы.


Первое, что нужно сделать, прежде чем начать писать код, это посетить конечную точку одного из API и посмотреть, что вы будете получать. Давайте отправим GET-запрос в Random User Generator, просто для проверки. Вы получите что-то подобное:


image


Это называется JSON-файл (JavaScript Object Notation), который должен выглядеть для вас знакомым, если вы когда-либо использовали объекты в JavaScript. Давайте запросим тот же ресурс программным способом:


// Отправляем 'GET'-запрос на заданный URL и после его завершения исполняем callback-функцию
function httpGetAsync(url, callback) {
  var xmlHttp = new XMLHttpRequest();
  xmlHttp.onreadystatechange = function() {
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
      callback(xmlHttp.responseText);
  };
  xmlHttp.open('GET', url, true);
  xmlHttp.send(null);
}

Именно это и делает приведённый код, который занимает всего 10 строк (9 без комментария). Скармливаем соответствующую URL конечную точку API и функцию callback, и мы увидим какой-то контент. Допустим, вы создали пустую HTML-страницу и залинковали вышеприведённый JS-код. Теперь просто выполним её в браузерном инструментарии для разработчиков:


httpGetAsync(
  'https://randomuser.me/api/',
  function(e){console.log(e)}
);

Вы получите результат, аналогичный предыдущему, только теперь он будет обёрнут в двойные кавычки. Естественно, ведь это строковое значение. И что ещё естественнее, теперь мы можем конвертировать его в JS-объект посредством JSON.parse(), а затем использовать так, как предполагалось.


Мы только что получили от API какой-то контент и вывели его в консоли. Прежде чем пойти дальше, давайте немного изучим этот API, чтобы впоследствии эффективнее его использовать.


В частности, я бы сосредоточился на параметрах seed и results, которые можно встроить в наш URL, чтобы каждый раз получать тот же набор пользователей. Для их добавления нужно просто добавить после URL знак ?, а затем {parameter_name}={value}, разделяя параметры с помощью &. Полагаю, все об этом знают, но всё же нужно было об этом упомянуть. В этой статье я также воспользуюсь параметром nationalities, чтобы быть уверенным, что в HTML не будет не-латинских символов, которые усложнят мне жизнь. Отныне и впредь, моей целевой конечной точкой для получения пользователей будет эта, она поможет мне получить тех же 25 пользователей, и всех из США.


TL;DR: Чтобы начать разрабатывать веб-приложение, нужно найти высококачественный JSON API. С помощью 10 строк JavaScript-кода можно легко запрашивать ресурсы у RESTful API, которые выдают случайных пользователей, и потом конвертировать их в JavaScript-объекты.


Представление и головоломка с CSS-фреймворком


Решив задачу получения какого-то контента от API, мы теперь можем отрисовать его, чтобы показать пользователю. Но работа с CSS-стилями и селекторами может быть той ещё головной болью, так что многие прибегают к помощи CSS-фреймворка. Весьма популярен Bootstrap, он хорошо задокументирован, имеет большое и приятное сообщество, и богат своими возможностями. Но я воспользуюсь mini.css, который разработал сам и знаю лучше, чем Bootstrap. Подробнее об этом фреймворке можете почитать в других моих статьях.


Инструкции по использованию классов и селекторов фреймворка применимы только к mini.css, но с очень небольшими правками они будут верны и для Bootstrap (считайте представление веб-приложения своей домашней работой, потому что здесь я не буду вдаваться в подробности относительно CSS).


Прежде чем мы сможем что-то вывести на экран, нам нужно создать оболочку приложения. Ничего особенного, только панель навигации и маленький заголовок, ну ещё может быть футер. Я воспользуюсь элементом <header>, который уже присутствует в mini.css, но вы можете решить эту задачу так, как вам удобнее. Также я добавлю в панель навигации .drawer, который может быть заменён какими-нибудь кнопками, и, наконец, <footer>, чтобы приложение выглядело чисто и аккуратно:


image


Я буду понемногу показывать свой код (после добавления карточки пользователя), но сначала скажу, почему использование CSS-фреймворка является хорошей идей. Попросту говоря, когда вы строите для веба, то существует слишком много тестовых вариантов, и некоторые комбинации устройства/ОС/браузера получаются более своеобразными, чем другие. CSS-фреймворк работает с этим особенностями по своему усмотрению, хотя в то же время предоставляет вам базовый каркас, который поможет создать адаптивное приложение (responsive application). Адаптивность крайне важна в разработке для мобильных устройств, так что почитайте об этом побольше.


Допустим, нас всё устраивает в оболочке приложения, давайте перейдём к отрисовке данных, полученных от Random User Generator. Я не собираюсь усложнять эту процедуру и выведу для каждого пользователя имя, изображение, ник, почту, местоположение и день рождения. Но вы можете придумать свой набор позиций. Не забудьте обратиться к ключу .results вашего объекта после парсинга JSON-данных, потому что в него всё обёрнуто. Чтобы всё выглядело красиво, я воспользуюсь замечательным пакетом иконок Feather, а также классом .card из mini.css и кое-какими CSS-трюками.


Пример приложения с карточками случайно сгенерированных пользователей.


Теперь наше приложение динамически заполняется контентом, который мы выводим на экран с помощью JavaScript и HTML. Мы только что создали наше первое представление (View), являющееся причудливым способом сказать, что мы создали визуальное отображение данных, запрошенных у API.


TL;DR: Отзывчивость и стили крайне важны для любого веб-приложения, так что будет хорошей идеей использовать CSS-фреймворк для создания простой HTML-оболочки приложения, а затем отрисовки с помощью JavaScript данных, полученных от API.


JavaScript-библиотеки


Наш макет веб-приложения работает хорошо, но JavaScript-код, особенно метод renderUsers(), получился довольно запутанным и выглядит очень трудным в сопровождении. К счастью, есть хороший способ сделать всё то же самое, но с помощью инструмента, которым сегодня пользуются все клёвые пацаны — React. Если кто-то из вас о нём не слышал: это очень мощная JavaScript-библиотека, сильно облегчающая отрисовку и обновление страницы, при этом повышая скорость её работы с помощью виртуального DOM и методики, известной как diffing. В сети есть куча толковых публикаций на этот счёт.


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


Также React позволяет использовать JSX, благодаря чему мы можем писать HTML внутри JavaScript без необходимости оборачивать его в кавычки. Так что наш код может стать ещё немного чище. Однако помните, что придётся сделать некоторые преобразования, например из class в className. Но со временем вы привыкните, а отладочные сообщения в консоли действительно очень помогают в решении таких проблем.


Я также взял на себя смелость преобразовать три инлайненых SVG в их собственные функции, так что теперь весь процесс отрисовки выглядит так:


// Функциональный компонент для иконки почты.
function SvgMail(props){
  return <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#424242" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>;
}
// Функциональный компонент для иконки календаря.
function SvgCalendar(props){
  return <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#424242" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>;
}
// Функциональный компонент для иконки прикрепления к карте.
function SvgMapPin(props){
  return <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#424242" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>;
}
// Функциональный компонент для иконки пользовательской карточки.
function UserCard(props){
  var user = props.user;
  return <div className="col-md-offset-1 col-lg-offset-2">
    <div className="card fluid" id={"user_"+user.login.username}>
      <div className="section row">
        <div className="col-sm-2 col-md-1">
          <img src={user.picture.medium} alt={user.login.username} className="user-image" />
        </div>
        <div className="col-sm">
          <h4 className="user-name">{user.name.title} {user.name.first} {user.name.last}
            <small>{user.login.username}</small>
          </h4>
        </div>
      </div>
      <div className="section">
        <p className="user-email"> <SvgMail />&nbsp;&nbsp;{user.email}</p>
      </div>
      <div className="section">
        <p className="user-birth"> <SvgCalendar />&nbsp;&nbsp;{user.dob.split(" ")[0].split("-").reverse().join("/")}</p>
      </div>
      <div className="section">
        <p className="user-location"> <SvgMapPin />&nbsp;&nbsp;{user.location.city}, {user.location.state}</p>
      </div>
    </div>
  </div>;
}
// Отрисовка списка пользователей в виде карточек.
function renderUsers(){
  var userCards = users.map(
    function(user, key){
      return <UserCard user={user} key={key}/>;
    }
  );
  ReactDOM.render( <div>{userCards}</div> ,contentArea);
}

Мы просто извлекли из предыдущего кода функциональный компонент, сделав его многократно используемой сущностью. Стоит взглянуть, что происходит под капотом, когда Babel преобразует HTML, предоставленный по вызовам React.createElement():


function SvgMapPin(props) {
  return React.createElement(
    'svg',
    { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: '#424242', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' },
    React.createElement('path', { d: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z' }),
    React.createElement('circle', { cx: '12', cy: '10', r: '3' })
  );
}

C другой стороны, если вы хотите начать с более лёгкой версии React, без JSX и некоторых более причудливых и сложных вещей, то я очень рекомендую обратиться к Preact, потому что это прекрасная альтернатива, которая может уменьшить время загрузки на одну-две секунды.


TL;DR: Преобразование с помощью React неструктурированных вызовов HTML-отрисовки в функциональные компоненты — задача простая, и результат получается гораздо удобнее в сопровождении. При этом разработчик получает больше контроля над своим кодом, и повышается степень повторного использования.


Добавляем второе представление (view)


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


  • Мы будем отправлять GET-запрос на адрес https://jsonplaceholder.typicode.com/comments?postId={id}, что позволит получить 5 сегментов текста за раз в JSON-формате, инкрементируя {id}.
  • Также мы будем отправлять GET-запросы на адрес https://unsplash.it/800/800?image={id}, при этом будем каждый раз получать какую-то картинку. Для этого воспользуемся кодом, который будет генерировать случайный {id} для каждого запроса. В моём Codepen-примере я также добавил массив неправильных значений {id}, которые я извлёк из API, так что у нас не будет ни одного ответа без изображения.
  • После выполнения обоих запросов будем создавать карточку для каждого поста, случайным образом приписывая пользователя из списка в качестве автора, и затем отрисуем с помощью React.
  • Также добавим кнопку переключения между двумя представлениями, чтобы иметь возможность перейти к списку пользователей. Я добавлю её в выпадающее меню, но вы можете делать на свой вкус.

После аккуратного кодирования всего упомянутого и последующего совершенствования, у вас получится нечто, схожее с Singe Page Application (SPA): https://codepen.io/chalarangelo/pen/zzXzBv


TL;DR: Чтобы ваш макет выглядел как настоящее веб-приложение, достаточно добавить второе представление и некоторые интерактивные элементы.


Загружаем ещё больше контента


Если вы уже занимались веб-разработкой, то сразу же заметите, чего не хватает в нашем приложении: бесконечной прокрутки. Её суть: когда вы пролистываете вниз до определённой границы, подгружается новая порция контента, и так происходит до тех пор, пока не кончится контент. В нашем примере мы установим границу в 80 %, но вы можете настроить этот параметр. Для этого нам понадобится очень простой метод, вычисляющий долю прокрутки страницы.


// Получает степень прокрутки страницы.
function getScrollPercent() {
  return (
    (document.documentElement.scrollTop || document.body.scrollTop) 
    / ( (document.documentElement.scrollHeight || document.body.scrollHeight) 
    - document.documentElement.clientHeight) 
    * 100);
}

Метод подсчитывает все кейсы и возвращает значение в диапазоне [0,100] (включительно). Если забиндить на window события scroll и resize, то можно быть уверенным, что когда пользователь достигнет конца страницы, то будет подгружен ещё контент. Вот как выглядит наше веб-приложение после добавления бесконечной прокрутки в представление с постами.


TL;DR: Бесконечная прокрутка — главное свойство любого современного веб-приложения. Она легка в реализации и позволяет динамически подгружать контент по мере необходимости.


Примечание: Если вы всё читаете и следуете всем рекомендациям, то сделайте 10-минутный перерыв, потому что следующие несколько глав довольно сложны и требуют больше усилий.


Манифест веб-приложения


Прежде чем мы сможем сказать, что действительно сделали веб-приложение (не говоря уже о прогрессивном веб-приложении), нам нужно поработать с файлом manifest.json, который состоит из кучи свойств нашего приложения, включая name, short_name, icons. Манифест предоставляет клиентскому браузеру информацию о нашем веб-приложении. Но прежде чем мы им займёмся, нам нужно с помощью npm и настройки React поднять приложение и запустить на localhost. Полагаю, что с обеими вещами вы уже знакомы и не столкнётесь с трудностями, поэтому переходим к манифесту.


По моему опыту, чтобы получилось работающее приложение, вам понадобится заполнить так много полей, что я предлагаю воспользоваться Web App Manifest Generator и заполнить, что хотите. Если у вас есть время, чтобы сделать аккуратную иконку, то в этом вам поможет Favicon & App Icon Generator.


После того как всё сделаете, ваш манифест будет выглядеть так:


{
  "name": "Mockup Progressive Web App",
  "short_name": "Mock PWA",
  "description": "A mock progressive web app built with React and mini.css.",
  "lang": "en-US",
  "start_url": "./index.html",
  "display": "standalone",
  "theme_color": "#1a237e",
  "icons": [
    {
      "src": "\/android-icon-48x48.png",
      "sizes": "48x48",
      "type": "image\/png",
      "density": "1.0"
    },
    {
      "src": "\/android-icon-72x72.png",
      "sizes": "72x72",
      "type": "image\/png",
      "density": "1.5"
    }
  ]
}

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


TL;DR: Файл manifest.json используется для определения конкретных свойств веб-приложения, прокладывая нам путь к созданию прогрессивного веб-приложения.


Сервис-воркеры (Service Workers)


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


Мы будем создавать самый простой вариант сервис-воркера, позволяющий кэшировать ответы на запросы, передавая их по мере доступности. Сначала создадим файл service-worker.js, который и будет нашим сервис-воркером.


Теперь давайте разберёмся с тремя основными событиями, с которыми нам придётся работать:


  • install генерируется при первой загрузке и используется для выполнения начальной установки сервис-воркера, вроде настройки оффлайн-кэшей.
  • activate генерируется после регистрации сервис-воркера и его успешной установки.
  • fetch генерируется при каждом AJAX-запросе, отправленном по сети, и может использоваться для обслуживания закэшированных ресурсов (особенно полезно при отсутствии сети).

Первое, что нам нужно сделать при установке, это воспользоваться CacheStorage Web API для создания кэша для веб-приложения и хранения любого статичного контента (иконок, HTML, CSS и JS-файлов). Это очень просто сделать:


// caches — это глобальная переменная только для чтения, являющаяся экземпляром CacheStorage
caches.open(cacheName).then(function(cache) {
  // Что-нибудь делаем с кэшем
});
// Источник: https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/open

Можно быстро создать простой обработчик события install, который кэширует наши index.html, JavaScript-файл и manifest.json. Но сначала нужно задать имя кэша. Это позволит нам отделить версии тех же файлов или данных от оболочки приложения. Здесь мы не будем вдаваться в подробности относительно кэшей, но в конце статьи есть полезные ссылки. Помимо имени кэша, нужно определить, какие файлы будут кэшироваться с помощью массива. Вот как выглядит обработчик install:


var cacheName = 'mockApp-v1.0.0';
var filesToCache = [
  './',
  './index.html',
  './manifest.json',
  './static/js/bundle.js'
];

self.addEventListener('install', function(e) {
  e.waitUntil(caches.open(cacheName)
    .then(function(cache) {
      return cache.addAll(filesToCache)
        .then(function() {
          self.skipWaiting();
        });
      }));
});

Меньше чем в 20 строках мы сделали так, что наше веб-приложение использует кэш для своих ресурсов. Позвольте пояснить оду вещь. В ходе разработки наш JS-код компилируется в файл ./static/js/bundle.js. Это одна из причуд нашего development/production-окружения, и мы разберёмся с ней на следующем этапе.


Обработчик activate тоже довольно прост. Его главная задача: обновлять кэшированные файлы, когда что-то меняется в оболочке приложения. Если нам нужно обновить любые файлы, то нам придётся изменить cacheName (желательно в стиле SemVer). Наконец, обработчик fetch проверит, хранится ли в кэше запрошенный ресурс. Если он там есть, то будет передан из кэша. В противном случае ресурс будет запрошен как обычно, а ответ будет сохранён в кэше, чтобы его можно было использовать в будущем. Соберём всё вместе:


self.addEventListener('activate', function(e) {
  e.waitUntil(caches.keys()
    .then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (key !== cacheName)
          return caches.delete(key);
      }));
  }));
  return self.clients.claim();
});

self.addEventListener('fetch', function(e) {
  e.respondWith(caches.match(e.request)
    .then(function(response) {
      return response || fetch(e.request)
        .then(function (resp){
          return caches.open(cacheName)
            .then(function(cache){
              cache.put(e.request, resp.clone());
              return resp;
          })
        }).catch(function(event){
          console.log('Error fetching data!');
        })
      })
  );
});

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


if ('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('./service-worker.js')
    .then(function() { console.log('Registered service worker!'); });
}

После всех настроек, откройте в Chrome Dev Tools окно Application и посмотрите, всё ли работает как нужно. Сервис-воркер должен правильно зарегистрироваться, активироваться и запуститься. Если пролистаете вниз, поставите галочку Offline и обновите, то начнёт работать оффлайн-версия страницы, использующая закэшированные ресурсы.


image


Мы только что превратили наше веб-приложение в прогрессивное, но разработка ещё не закончена. Последним шагом будет сборка для production.


TL;DR: Сервис-воркеры позволяют веб-приложениям настраивать кэши и использовать их для загрузки ресурсов без использования сети, превращая веб-приложения в прогрессивные.


Сборка для production


Всё, что мы сделали, прекрасно работает на localhost, но осмотр production-файлов после быстрого выполнения npm run build обнаруживает кучу ошибок, которые нужно исправить. Прежде всего, многие файлы неправильно залинкованы на/из HTML-страницы. Для решения этой проблемы нужно добавить одну строку в package.json:


"homepage" : "."

После пересборки и открытия HTML-страницы в браузере мы видим, что большая часть заработала правильно. Добавив строку со свойством "homepage", мы сказали системе собрать скрипт так, чтобы в ссылках все пути были изменены на ., то есть теперь они являются относительными путями.


Следующая проблема: наш сервис-воркер указывает на неправильные JS-файлы, потому что static/js/bundle.js больше не существует. Если внимательнее посмотреть на собранные файлы, то станет понятно, что наш JS был преобразован в файл main.{hash}.js, где часть {hash} отличается для каждой сборки. Нам нужно иметь возможность загружать из сервис-воркера файл main.js. Следовательно, нужно переименовать его, но этого сломает ссылку внутри index.html, чего мы не хотим. Для решения обеих проблем воспользуемся сборочными инструментами из реестра npm — renamer и replace. Также нам нужно минифицировать наш service-worker.js, поскольку по умолчанию он не слишком компактен, потому что является частью папки public, так что воспользуемся ещё и утилитой uglify-js.


npm install --save-dev renamer
npm install --save-dev replace
npm install --save-dev uglify-js

Три простые команды, и мы получаем нужные нам инструменты. Пользоваться ими сравнительно легко после ознакомления с документацией, так что я продолжу и покажу, как выглядит свойство "scripts" в моём package.json после создания всех необходимых скриптов.


"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build && npm run build-rename-replace && npm run build-sw",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "build-sw" : "npm run build-replace-sw-refs && npm run build-minify-sw",
    "build-rename-replace": "npm run build-rename && npm run build-replace",
    "build-rename": "npm run build-rename-js && npm run build-rename-css",
    "build-rename-js": "renamer --regex --find main\\.\\w+\\.js --replace main.js build/static/js/*.js",
    "build-rename-css": "renamer --regex --find main\\.\\w+\\.css --replace main.css build/static/css/*.css",
    "build-replace": "npm run build-replace-js && npm run build-replace-css",
    "build-replace-js": "replace main\\.\\w+\\.js main.js build/index.html",
    "build-replace-css": "replace main\\.\\w+\\.css main.css build/index.html",
    "build-replace-sw-refs": "replace './static/js/bundle.js' './static/js/main.js','./static/css/main.css' build/service-worker.js",
    "build-minify-sw" : "uglifyjs build/service-worker.js --output build/service-worker.js"
  }

Давайте разберёмся, что тут происходит:


  • build-rename запускает два скрипта, каждый из которых меняет имена JavaScript- и CSS-файлов, которые были созданы с желаемыми наименованиями (main.js и main.css соответственно).
  • build-replace запускает два скрипта, каждый из которых меняет меняет ссылки на JavaScript- и CSS-файлы на переименованные версии.
  • build-rename-replace собирает предыдущие две команды в одну.
  • build-sw обновляет в сервис-воркере ссылку на static/js/bundle.js, которая теперь указывает на новый файл main.js, а также добавляет ссылку на main.css. Затем минифицирует сервис-воркер.
  • Наконец, build собирает вместе всё вышеперечисленное в наш процесс сборки по умолчанию, так что все файлы попадают в папку build и готовы к выкладыванию на сайт.

Теперь при запуске npm run build должна генерироваться готовая к production версия нашего прогрессивного веб-приложения, которую можно хостить где угодно, просто копируя файлы из папки build.


TL;DR: Создание веб-приложения для production является интересной проблемой, требующей использования сборочных инструментов и разрешения скриптов. В нашем случае достаточно было обновить имена и ссылки.


Последние шаги


Это был трудный путь, но теперь наше прогрессивное веб-приложение готово к production. Прежде чем поделиться им с миром, выложив куда-нибудь на Github, хорошо бы оценить его качество с помощью инструмента Lighthouse, который выставляет приложению баллы и выявляет проблемы.


После прогона моей development-сборки на localhost я обнаружил несколько моментов, требующих исправления, вроде того, что у картинок нет атрибутов alt, внешние ссылки не имеют атрибута rel="noopener", а само веб-приложение не имеет географической привязки (landmark region), что было легко исправлено с помощью HTML-тега <main>. Исправив всё, что мог, я получил такой результат в Lighthouse:


image


Как видите, приложение получилось хорошо оптимизированным и качественным. Теперь я могу с гордостью выложить его на Github. Вот ссылка на законченное приложение; а вот ссылка на исходный код. Я взял на себя смелость добавить индикаторы Online/Offline, но вы можете их выкинуть. Теперь я могу установить приложение на смартфон и показать друзьям!


image


image


image


TL;DR: Прежде чем релизить веб-приложение, стоит проверить его качество с помощью Lighthouse и оптимизировать, насколько возможно.


Вот и всё мы создали с нуля прогрессивное веб-приложение. Надеюсь, вы чему-то научились. Спасибо за уделённое время!


Ссылки


  • MDN’s Getting started with AJAX — отличный ресурс для тех, кто хочет больше узнать об AJAX и как его использовать в веб-приложениях.
  • Если выбираете JSON API, то вас могут заинтересовать эти:
    UI Names — ещё один ресурс для генерирования случайных пользователей с большим количеством настроек.
    Robohash — генерирует аватары роботов на основании заданной строки. Удобно при создании пользовательских профилей или placeholder-изображений.
    Adorable Avatars? — генерирует смешные аватары на основании заданной строки. Настроек чуть меньше, чем в Robohash, но задачу выполняет.
  • Из огромной экосистемы CSS-фреймворков можно выделить такие популярные продукты, как Bootstrap, Semantic UI, Bulma и Foundation. Предлагаю и свою альтернативу, mini.css, если вам нужно что-то более лёгкое.
  • Для UI-иконок обычно берут FontAwesome, но я рекомендую Feather, более лёгкие и минималистичные.
  • Если хотите больше узнать о diffing виртуального DOM, то очень рекомендую почитать эту статью.
  • Среди всех современных JavaScript-библиотек, React является самой популярной. Но хороши также Preact и VueJS, выбирайте, что больше нравится.
  • Манифесты веб-приложений хорошо задокументированы на сайтах MDN и Google Developers. Также можете воспользоваться Web App Manifest Generator, если хотите легко и быстро сгенерировать manifest.json.
  • Говоря о сервис-воркерах и прогрессивных веб-приложениях, я очень рекомендую создавать своё первое приложение так, как описано здесь и здесь. Также обратите внимание на жизненный цикл сервис-воркера. А если хотите заняться этой темой ещё плотнее, почитайте Indicating Offline (я его использовал для создания Online/Offline-индикаторов) и Offline Recipes for Service Workers.
  • Относительно CacheStorage Web API я рекомендую почитать эту статью, а также а также держать под рукой документацию MDN.
  • Если хотите оценить своё веб-приложение и исправить найденные проблемы, воспользуйтесь Lighthouse и Mobile Speed Test.
  • Наконец, изучите исходный код и демку на Github.
Поделиться с друзьями
-->

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


  1. ambientos
    01.08.2017 21:31
    +8

    > Когда фронтендеры наконец откажутся от джиквэри?
    Инструмент решает бизнес-задачи, зачем от него отказываться?


    1. PaulZi
      01.08.2017 23:45

      jQuery может умереть только если умрут множество плагинов написанных на нём типа selectric, selectize, slick и прочих популярных. Сейчас, конечно, понимаешь что можно писать код спокойно без jQuery, но всё-равно придётся его подключать из-за какого-нибудь плагина. А если он есть, зачем писать `forEach(item => { item.addEventListener(); })` если можно тоже самое писать существенно короче?


      1. franzose
        02.08.2017 07:32
        -1

        А если он есть, зачем писать forEach(item => { item.addEventListener(); }) если можно тоже самое писать существенно короче?

        Так писать и не надо. Надо писать примерно так:


        document.getElementById('elem-id').addEventListener('click', (event) => {
            if (event.target.classList.contains('js-item-class')) {
                // здесь как-то обрабатываем
            }
        });


        1. petuhov_k
          02.08.2017 08:09
          -2

          Во-первых, зачем проверку на класс делать внутри, если есть document.querySelector и document.querySelectorAll.

          Во-вторых, document.getElementById вернёт всего один элемент — конечно forEach не нужен. Плюс jQuery в том, что позволяет подписаться сразу на всё без foreach.

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


          1. franzose
            02.08.2017 08:46
            +1

            Если у вас будет 20 элементов с классом .js-item на странице, то в обычном случае мы добавим 20 обработчиков событий. А я привёл фрагмент, в котором обработчик клика добавляется на контейнер, а внутри уже проверяется, по какому элементу произошел клик. Как итог, обработчик всего один. Прошу прощения, если сразу недоступно объяснил намерения)


            1. PaulZi
              02.08.2017 09:01
              +2

              Собственно, суть была именно в том, что в jQuery удобно работать с коллекциями, хотя бы тот же toggleClass() применить.


  1. eavorobiev
    01.08.2017 21:48

    всё это упаковывается в Android WebView, правильно я понимаю?


  1. Eldhenn
    01.08.2017 22:13
    +44

    > Бесконечная прокрутка — главное свойство любого современного веб-приложения

    Как же мы вас ненавидим…


    1. andreyyka
      01.08.2017 23:35
      -3

      А чем столь ужасна бесконечная прокрутка, если её не прокручивать далеко в дебри?


      1. Immortal_pony
        01.08.2017 23:37
        +5

        Сложности с предоставлением ссылки на определенную страницу, например.


        1. Areso
          02.08.2017 07:01

          Ссылки на определенную страницу и раньше были местами проблематичны.
          Смотрите, я создал сайт с первой страницей, там 10 новостей. Я даю ссылку на первую новость, и выглядит она как mysite.com/page1/
          Я добавляю еще одну новость (11-ую) и первая новость из первой десятки съезжает на mysite.com/page2/


          1. Caravus
            02.08.2017 11:07
            +1

            Эта проблема решается ссылками не «открыть страницу Х от начала» а «показать новости начиная с Х и старше».


            1. edge790
              02.08.2017 17:25
              +1

              проблему с "бесконечной прокруткой" тоже можно аналогичным образом


      1. evgenWebm
        01.08.2017 23:56
        +9

        Добавлю еще.
        Вечная прокрутка, это 100% гарантия тормозов на сайте.
        Не у всех на компах стоят хотябы 8гб памяти(да да).


        1. raveclassic
          02.08.2017 01:08
          -3

          Нет, не 100%, если сделана нормально через виртуальный скроллинг.


          1. raveclassic
            04.08.2017 22:55

            Ну расскажите что-ли, что не так?


      1. Eldhenn
        02.08.2017 07:36
        +2

        А зачем она нужна, если её «не прокручивать далеко в дебри»?
        И потом. Листаю, эээ, прокручиваю я новости. Август, июль, июль, июль… мне нужен февраль месяц, я точно помню, что это было в феврале.


        1. raveclassic
          02.08.2017 09:56

          Добавьте на страницу выдачи фильтр по дате, чтобы пользователь себе колесико мышки не сломал. Бесконечная прокрутка — это бесконечно удобная вещь, если сделана правильно и в сопровождении нужных фильтров.


          1. andreysmind
            02.08.2017 13:38

            И ее бесконечно сложно сделать в отличии от простой пагинации одним плагином.


            1. raveclassic
              02.08.2017 13:42

              Ну тут уж выбор за вами — UX vs DX


          1. Eldhenn
            02.08.2017 15:27
            +5

            Фильтр по дате — не панацея. Вы никогда не листали бумажную книгу в поисках «где-то здесь»? «Примерно на середине» — открываем на середине. Читаем две строчки — «ага, сильно раньше». Отлистываем три десятка страниц, читаем две строчки — «чуть позже».

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


            1. raveclassic
              02.08.2017 15:57

              Ну вы же явно что-то конкретное ищите "где-то здесь". Что-то такое альфанумерик. Ну так вот вам текстовое поле для поиска того, что вы ищите, вместо того, чтобы тыкаться по страницам туда-сюда.


              1. Eldhenn
                02.08.2017 16:04

                Да, я ищу что-то такое, что я узнаю с первого взгляда.
                Вы никогда не искали книжку в синей обложке? Никогда не находили её — в коричневой?


                1. raveclassic
                  02.08.2017 16:06

                  Эмм… знаете, если у вас 100 страниц по 20 одинаковых синих книг на каждой, то как вы посередине их отличите друг от друга? Есть еще какой-то критерий поиска?


                  1. Eldhenn
                    02.08.2017 18:37
                    +2

                    Я описал три не совсем идентичных кейса. Все они противоречат бесконечной прокрутке.
                    Какой кейс решает прокрутка, кроме "останься здесь ещё на пять минут, посмотри котиков, посмотри рекламу, останься ещё на пять минут, посмотри..."?


                    1. raveclassic
                      02.08.2017 18:44

                      Вы пока не описали, что пользователь будет делать посреди выдачи с синими книгами. Бесцельно щелкать по страницам? Если ему надо найти в магазине Марка Твена — пожалуйста, вот поиск или оглавление на худой конец. Не представляю, что он забыл именно на 30ой странице этой выдачи. Интересно полистать, что есть — ну вот, листай с самого начала, еще и по популярности отсортировано.


                      "останься здесь ещё на пять минут, посмотри котиков, посмотри рекламу, останься ещё на пять минут, посмотри..."

                      А вот такие рассуждения не несут полезной нагрузки.


      1. Aingis
        02.08.2017 13:05
        +8

        Читаю я, например, Пикабу на мобильном устройстве. Беру самое популярное за месяц. Там десятки страниц. Стоит случайно перейти по ссылке куда-то (или переключиться на другие задачи) и вернуться назад — всё сбросилось, читай крути сначала. А там ещё и подгрузка тормозная. И это ещё оптимизировали, раньше вообще тормозило на сотне постов.

        И эту проблему с перезагрузками на сайте не решить вообще никак. Проще бросить это бесполезное дело.


        1. Skerrigan
          03.08.2017 07:59

          Я точно видел рабочий пример, где бесконечный скролл, но при «уйти, затем вернуться», адресовало точно туда же, откуда ушел.


          1. Aingis
            03.08.2017 12:53

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


            1. Skerrigan
              04.08.2017 05:48

              Не-не-не, не думайте пожалуйста, что я предлагаю вам что-то пилить, разумеется это должен делать сайт. Я имел в виду то, что это «физически» возможно, т.е. это не миф.


    1. scronheim
      02.08.2017 07:00
      +1

      Жаль не могу влепить Вам плюсик. Бесконечная прокрутка это бич современных веб-приложений


      1. Odrin
        02.08.2017 09:50
        -1

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


        1. raveclassic
          02.08.2017 09:56

          О, опередили меня :)


      1. herr_kaizer
        02.08.2017 18:38
        +1

        Бич современного веба — это веб-приложения где не надо. Дожили, с noscript почту в браузере иной раз не посмотреть.


        1. raveclassic
          02.08.2017 18:45

          За вами видимо гугл следит через js?


          1. PerlPower
            02.08.2017 21:07
            +1

            Ох если бы он следил только через JS…

            А по сути — носкрипт приходится использовать чтобы банально было меньше тормозов, хотя у меня процессор под 4 ГГц и 32 гб RAM. У вашего провайдера/на работе заблокирован какой-то CDN или сайт типа facebook/vk/linkedin, которые много кто использует для авторизации на своих сайтов — можете ждать загрузки даже самого простого сайта очень долго.


    1. Nagg
      03.08.2017 12:56

      Особенно когда там есть футер :D по-моему у вебгуёвого инстаграмма так


  1. Immortal_pony
    01.08.2017 23:33
    +6

    Я не понял из статьи, зачем нужен React, котгда все это можно сделать с помощью JQuery.
    Кто-нибудь может подробнее объяснить?


    1. evgenWebm
      01.08.2017 23:56

      Может дело вкуса?


    1. mSnus
      02.08.2017 03:17
      -1

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


      Ну не делать же всего день в одиночку на JQuery то, что можно сделать командой за месяц!


      1. defaultvoice
        02.08.2017 03:55
        +9

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

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


        1. Immortal_pony
          02.08.2017 05:02
          -1

          Макаронный код на JQuery никто писать не заставлет.

          познаете всю боль поддержки сайтов с более-менее замороченной логикой на клиенте.

          Почему нельзя вынести «тяжелую» часть на сервер?

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

          Я, в основном, занимаюсь backend'ом, но периодически приходится делать и frontend. И вот работал я и CRM-системами и немаленькими, и с backoffice'ами заковыристыми, и с интернет-магазинами и просто со сложными сайтами-каталогами, но так и ни разу и не почувствовал, что на клиенте мне не хватает инструментария Bootstrap+JQuery. Если не сложно, разъясните, что я упускаю и в каких случаях время, потраченное на освоение framework'а типа React/Vue/Angular мне удастся сэкономить при дальнейшей разработке с его помощью?


          1. raveclassic
            02.08.2017 10:01
            +1

            Попробуйте напишите что-нибудь больше похожее на приложение. Лучше даже backendless, чисто in-memory. Вы офигеете делать это на jquery, а если и не офигеете сразу, то офигеете через пол года на поддержке.


            Вся эта "ненужная возьня" с ангулярами и реактами предоставляет должный уровень абстракции для создания приложений. Так-то вы на голом бэкендом языке (любом) руками слушать 80-ку и руками писать туда html строкой. Но вы же явно этого не делаете.


            1. Odrin
              02.08.2017 10:07
              +2

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


              1. andreysmind
                02.08.2017 13:43
                +3

                Одни пишут чистые компоненты, другие классы, третий все старается писать иммутабельно и функциями. Кто-то использует redux, еще кто-нибудь предпочитает MobX или что еще там.
                Угу. Очень стандартно и понятно.


            1. raveclassic
              02.08.2017 10:21

              Что-то я еще не проснулся… Правильно будет:


              Так-то вы на голом бэкендном языке (любом) можете руками слушать 80-ку и руками писать туда html строкой.


              1. Immortal_pony
                03.08.2017 00:37
                -1

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


                1. Caravus
                  03.08.2017 12:05

                  Ну… фронтендеры тоже хотят кушать…


                1. raveclassic
                  03.08.2017 12:48

                  Зачем раздувать код на бэке, когда можно все сделать на фронте?


                  1. Free_ze
                    03.08.2017 13:27

                    Если делать все на фронте, то веб будет не нужон.


                    1. raveclassic
                      03.08.2017 14:23

                      Ну как же, бэк для хранилища и тяжелых расчетов, фронт для удобной функциональной морды.


                      1. Eldhenn
                        03.08.2017 14:36

                        SPA с бесконечной прокруткой?


                        1. raveclassic
                          03.08.2017 14:39

                          А как же, все как вы любите


                      1. Free_ze
                        03.08.2017 15:19

                        Сначала вы говорите «всё», потом оказывается, что это «весь UI».


                        1. raveclassic
                          03.08.2017 15:46

                          Так а это ответ на


                          Зачем раздувать код на фронте, когда можно все сделать на бэке?

                          такой вот


                          Зачем раздувать код на бэке, когда можно все сделать на фронте?


                          1. Caravus
                            03.08.2017 17:36

                            Как сделать «всё на бэке» я ещё представляю, хотяб тот же голый html отдавать, без JS вообще, если уж спорить. А вот как сделать «всё на фронте»?


                            1. raveclassic
                              03.08.2017 17:55

                              Ну как, делаете толстый клиент, а в качестве фронтенда системы пишете какой-нибудь REST.


                              1. Caravus
                                03.08.2017 17:58

                                Странное у вас понимание «всего».


                                1. raveclassic
                                  03.08.2017 18:01

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


                                  1. Free_ze
                                    03.08.2017 18:23

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


                                    1. raveclassic
                                      03.08.2017 18:38

                                      Так ветка-то не с таких посылов началась, а с гораздо более прозаичных.


                                      1. Free_ze
                                        03.08.2017 19:13

                                        Вы прямым текстом заявили: «можно все сделать на фронте».
                                        Никакой прозы, 100% холивара (=


                  1. Immortal_pony
                    03.08.2017 18:56

                    Чтобы снизить нагрузку на клиентские устройства.
                    Чтобы уменьшить количество потенциальных дыр в безопасности.
                    Чтобы у клиента не возникло проблем с актуальностью данных.


            1. Immortal_pony
              03.08.2017 00:14

              Попробуйте напишите что-нибудь больше похожее на приложение.

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

              Лучше даже backendless

              Зачем?


              1. Skerrigan
                03.08.2017 08:09

                С одной стороны хочется сказать «да не зачем, просто жутко модно забивать на бекенд, все пихать в клиент, потом делать вид, что не слышим жалоб пользователей о страничках с 50Мб».
                С другой, ну вот таки да — если юзер может как-то более продвинуто работать с веб-приложением, тогда просто заморочаемся все описывать именно на беке. Ибо когда мы можем что-то просто отrhыть/сабмитнуть, тогда да, как бы бекенд справится. При ajax даже сможем отобразить изменения без ребута страницы.
                Однако когда у нас идет строжайшее разделение, тогда html только каркасом, все без исключений стили и практически все до единого вопросы анимации/стилей на css (preprocessing only), RESTful API — передача только самих данных, тогда как раз и наступает время реактивизации фронтенда. И там уже по полной прут локальные хранилища для сохранения всевозможного состояния GUI, как раз пример с прокруткой бесконечной (возврат после кнопки «назад» куда надо), drag&drop, современные модальные окна, вебсокеты… все это без реактивизации почти на порядок сложней.


                1. Immortal_pony
                  03.08.2017 19:25

                  До слована «однако» я прекрасно понимаю ваш комментарий, однако далее для меня как будто теряется нить повестования :)

                  Однако когда у нас идет строжайшее разделение

                  Разделение чего с чем?

                  тогда html только каркасом, все без исключений стили и практически все до единого вопросы анимации/стилей на css (preprocessing only)

                  Это какие-то вопросы разметки. HTML — каркас, в CSS — стили и анимация. И?

                  RESTful API — передача только самих данных, тогда как раз и наступает время реактивизации фронтенда.

                  Почему только для передачи данных? Даже RESTful API можно использовать для получения частей страницы, а не только данных.

                  И там уже по полной прут локальные хранилища для сохранения всевозможного состояния GUI

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

                  прокруткой бесконечной (возврат после кнопки «назад» куда надо), drag&drop,
                  современные модальные окна

                  Это вопрос подключения библиотеки и 10 строк инициализации+конфига.

                  вебсокеты

                  Это вообще немного отдельная тема.


              1. raveclassic
                03.08.2017 12:51

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

                Ну где же вы пробовали? Вы на каждый чих на бэк лазите. Вопрос не в том, нужны толстые клиенты или нет. Вопрос в том, напишите ли вы такой на jquery.


                1. Immortal_pony
                  03.08.2017 19:05

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

                  Вы на каждый чих на бэк лазите

                  Ну и что? С точки зрения пользователя что-то меняется?

                  Вопрос не в том, нужны толстые клиенты или нет.

                  Да нет, это как раз основной мой вопрос. Какие плюсы от толстых клиентов в web'е?


                  1. defaultvoice
                    03.08.2017 19:40

                    С точки зрения пользователя что-то меняется?

                    Отзывчивость, притом, достаточно серьезно.


                    1. Immortal_pony
                      03.08.2017 20:55
                      -1

                      Ну да, при толстом клиенте она будет хуже.

                      Любое действие пользователя все равно спровоцирует запрос на сервер (данные получить/сохранить — тут без сервера никак). С точки зрения отзывчивости отрендерить данные на клиенте будет ничуть не быстрее чем вставить предварительно отрендеренные на сервере данные. Скорее всего даже медленне.
                      Также JS на клиенте ухудшит отзывчивость.


                      1. raveclassic
                        03.08.2017 21:36
                        +1

                        С точки зрения отзывчивости отрендерить данные на клиенте будет ничуть не быстрее чем вставить предварительно отрендеренные на сервере данные. Скорее всего даже медленне.
                        Также JS на клиенте ухудшит отзывчивость.

                        Что за ахинея.


                        1. Immortal_pony
                          03.08.2017 22:53

                          Обоснуйте.

                          PS

                          Также JS на клиенте ухудшит отзывчивость.

                          Следует читать как
                          Также большой объем JS на клиенте ухудшит отзывчивость.


                          1. raveclassic
                            03.08.2017 23:04

                            Вроде базовые вещи… ну да ладно.


                            Чтобы запросить новую разметку нужно время, чтобы отрендерить ее нужно время, чтобы передать обратно на клиент нужно время, чтобы распарсить html и вставить в dom нужно время. Учитывая в каком плачевном состоянии может находиться соединение с интернетом, все это будет яно дольше, чем точечная работа с DOM и запросы данных небольшими порциями тогда, когда нужно.


                            У вас видимо свое понятие отзывчивости, так как на отзывчивость размер js не влияет. К тому же все толстые скрипты прекрасно кэшируются, а парсятся только один раз на старте приложения.


                            1. Immortal_pony
                              03.08.2017 23:27

                              Чтобы запросить новую разметку нужно время,
                              распарсить html и вставить в dom нужно время

                              Так на запрос/получение данных столько же времени уходит.
                              Также толстому клиенту часто требуется несколько запросов для получения данных. Более того — несколько зависимых друг от друга (то есть синхронных) запросов. Что будет в разы дольше, чем получить сразу отрендеренный кусок страницы от сервера.

                              чтобы отрендерить ее нужно время

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

                              распарсить html и вставить в dom нужно время

                              А сгенерировать html и вставить его быстрее? В любом случае, не припомню, чтобы я видел затруднения именно в этом месте. Это какие-то миллисекунды.

                              У вас видимо свое понятие отзывчивости, так как на отзывчивость размер js не влияет. К тому же все толстые скрипты прекрасно кэшируются, а парсятся только один раз на старте приложения.

                              Дело не только в объеме кода, но в том, сколько этот объемный код потом выжрет памяти и процессорной мощности для своей работы.


                              1. raveclassic
                                04.08.2017 00:06

                                Так на запрос/получение данных столько же времени уходит.
                                Также толстому клиенту часто требуется несколько запросов для получения данных. Более того — несколько зависимых друг от друга (то есть синхронных) запросов. Что будет в разы дольше, чем получить сразу отрендеренный кусок страницы от сервера.

                                Но объем данных меньше, чем объем верстки. Если еще и пожать как-нибудь, через protobuf тот же.
                                Но не дай бог, если вы еще и всю страницу целиком перерендериваете.
                                А для тяжелых запросов выделяется отдельный ендпоинт в апишке.


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

                                Замеры, пожалуйста. Я уверен на 146%, что даже самый мощный сервер не спасет от latency. А при загрузке данных я прямо на клиенте мгновенно покажу спиннер и буду себе дожидаться ответа. Сохраняя при этом ту самую отзывчивость, так как пользователь может вообще свалить в другой раздел.


                                А сгенерировать html и вставить его быстрее?

                                Но зачем, если можно напрямую работать с DOM?


                                Дело не только в объеме кода, но в том, сколько этот объемный код потом выжрет памяти и процессорной мощности для своей работы.

                                Памяти может быть, процессорной мощности вряд ли, хотя конечно зависит от движка. "Поделок" достаточно. Но серьезный интерактив не бесплатен.


                                Что тут обсуждать вообще, все более-менее сложные веб-приложения сделаны именно как SPA. Office online, google docs, inbox, telegram, whatsapp… Не потому что разрабы хипстеры, а потому что это другой уровень как раз-таки той самой отзывчивости. Противиться этому по меньшей мере странно.


                                1. Immortal_pony
                                  04.08.2017 01:45

                                  Но объем данных меньше, чем объем верстки.

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

                                  Но не дай бог, если вы еще и всю страницу целиком перерендериваете.

                                  Зачем? Получаю кусок (или несколько) html от сервера — расставляю все по местам.

                                  А при загрузке данных я прямо на клиенте мгновенно покажу спиннер

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

                                  Office online, google docs, inbox, telegram, whatsapp

                                  Whtasapp? Telelgram? У них вообще нет онлайн-версии, только нативные приложения.
                                  Думаю, что приложения подобного уровня добиваются макимальной отзывчивости и реалтайма с помощью вебсокетов. Я с этой технологией как программист знаком слабо, так что не могу сказать, можно ли обойтись без framework'а или нет. Но а рамках данной дискуссии я бы хотел обсудить обычное клиент-серверное взаимодействие с AJAX-запросами.

                                  А сгенерировать html и вставить его быстрее?
                                  Но зачем, если можно напрямую работать с DOM?

                                  Не представляю, как это делать. Да и в примере в статье вроде html подготавливают.


                                  1. franzose
                                    04.08.2017 03:39

                                    У Телеграма есть веб-версия. https://web.telegram.org/


                                    1. Immortal_pony
                                      05.08.2017 01:55

                                      Видимо я что-то пропустил.
                                      Спасибо.


                                  1. Skerrigan
                                    04.08.2017 05:04

                                    AJAX-запросами


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

                                    Аякс по сути на сегодня — удел хобби/микро проектов.

                                    P.S. И да, тот же клиент whatsapp — это «хромиум» с веб-гуем внутри, если мне память не изменяет. Такое назвать «нативным» язык не повернется. Это SPA.


                                    1. Immortal_pony
                                      05.08.2017 01:54

                                      Вы сейчас говорите про SPA или вообще про интернет-порталы?


                                  1. raveclassic
                                    04.08.2017 10:42

                                    Если все это происходит асинхронно, то я могу понять выгоду от этого подхода

                                    Все запросы с клиента асинхронны.


                                    Зачем? Получаю кусок (или несколько) html от сервера — расставляю все по местам.

                                    То есть вы размазываете UI-логику, часть на клиенте, часть на сервере.


                                    Спиннер во время загрузки я тоже покажу.

                                    Предыдущий пункт.


                                    Whtasapp? Telelgram? У них вообще нет онлайн-версии, только нативные приложения.

                                    Выше уже ответили. Есть веб версии, а те, что десктопные — это электрон. (хотя насчет телеги не уверен).


                                    Думаю, что приложения подобного уровня добиваются макимальной отзывчивости и реалтайма с помощью вебсокетов. Я с этой технологией как программист знаком слабо, так что не могу сказать, можно ли обойтись без framework'а или нет. Но а рамках данной дискуссии я бы хотел обсудить обычное клиент-серверное взаимодействие с AJAX-запросами.

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


                                    Кроме того, все больше набирают популярность не классические (устаревающие?) запрос-ответ подходы (REST, RPC), а вещи из разряда graphql или даже pouchdb.


                                    Не представляю, как это делать. Да и в примере в статье вроде html подготавливают.

                                    Ну так это уже совсем другая история. Это не HTML, это JSX — описание маркапа на js. Потом там работает virtual dom рендерер.


                                    1. Immortal_pony
                                      05.08.2017 01:53

                                      Все запросы с клиента асинхронны.

                                      Разве? Я частенько при интеграции API сталкиваюсь с тем, что для достижения результата мне необходимо отправить сериию последовательных запросов.
                                      Вот, к примеру серия запросов к Fundamerica API для создания инвестиции:
                                      1. Создать предложение.
                                      2. Создать пользователя/компанию.
                                      3. Получить авторизацию.
                                      4. Создать инвестицию.
                                      И эти запросы невозможно отправить асинхронно — для отправки каждого следующего запроса мне необходимы данные из предыдущего.
                                      Вы не сталкиваетесь с такими ситуациями?

                                      То есть вы размазываете UI-логику, часть на клиенте, часть на сервере.

                                      Не совсем понимаю, что вы имеете ввиду под названием «UI-логика».
                                      Видимо, да, разделяю эту логику. И?

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

                                      Я имею ввиду, что я не разрабатывал клиентскую часть с использованием Websocket'ов и не могу сказать, есть ли реальная возможность не использовать клиентские framework'и при организации передачи данных через подобный канал. Подозреваю, что можно просто задействовать какую-то библиотеку и продолжать следовать описанным мною путем, но не могу утверждать этого точно.

                                      кроме того, все больше набирают популярность не классические (устаревающие?) запрос-ответ подходы (REST, RPC), а вещи из разряда graphql или даже pouchdb.

                                      А GraphQL — это не запрос-ответ по-вашему?
                                      Что касается pouchdb — я не знаком с этой технологией. Если я правильно понял, то данная технология позволяет писать данные в локальную ДБ, а потом синхронизировать данные с сервером, когда тот выходит онлайн.
                                      Если мое понимание верно, то такая технология породит целую тучу проблем, связанных с безопасностью и с ошибками синхронизации.

                                      Ну так это уже совсем другая история. Это не HTML, это JSX — описание маркапа на js. Потом там работает virtual dom рендерер.

                                      М, хорошо. Как я описал выше — проблем со скоростью в момент вставки подготовленного html я никогда не имел даже на самых слабых устройствах. Кроме увеличенной сложности есть какие-то плюcы у данного подхода?


                                      1. raveclassic
                                        05.08.2017 02:56

                                        И эти запросы невозможно отправить асинхронно — для отправки каждого следующего запроса мне необходимы данные из предыдущего.
                                        Вы не сталкиваетесь с такими ситуациями?

                                        Все запросы асинхронны. То, что вы их выполняете "последовательно", еще не значит что синхронно.
                                        Вот вам пример:


                                        async function transaction() {
                                          const result1 = await getResult1();
                                          const result2 = await getResult2(result1);
                                          //... etc
                                          return await getResultN(lastResult);
                                        }

                                        или по стариночке


                                        function transaction() {
                                          return getResult1()
                                            .then(getResult2)
                                            .then(getResult3)
                                            //... etc
                                            .then(getResultN)
                                        }

                                        Не совсем понимаю, что вы имеете ввиду под названием «UI-логика».
                                        Видимо, да, разделяю эту логику. И?

                                        Ну у вас бэк знает как рендерить одни куски UI, а клиент — другие (спиннеры и т.п.). В идеале я стремлюсь к тому, чтобы избавить бэк от ненужной работы и оставить ему те вещи, которые он действительно должен решать — сложную бизнес-логику. А симпотишную отзывчивую мордашку пусть рисует сам клиент, благо и возможностей, и ресурсов для этого 2017 году предостаточно.


                                        вебсокеты

                                        Тут никакие движки не нужны, апишка донельзя простая. Другое дело, что всякие разрывы, реконнекты и прочее придется ручками решать. Так что хоть библиотеку какую-нибудь можно взять, ту же socket.io


                                        GraphQL

                                        Да, я так и думал, что возникнут вопросы, но было уже поздно. Я имел в виду конкретный клиент и конкретный ui-кит под это дело — apollo и react. Вы можете декларативно объявить какие именно данные и какие поля вам нужны в заранее подготовленной схеме (модели). В качестве запросов остаются только мутации, которые тоже достаточно неплохо контролируются. При этом аполло кеширует запросы (когда нужно), батчит их, а графкл позволяет выбирать из сущностей только те поля, которые нужны.


                                        pouchdb

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


                                        М, хорошо. Как я описал выше — проблем со скоростью в момент вставки подготовленного html я никогда не имел даже на самых слабых устройствах. Кроме увеличенной сложности есть какие-то плюcы у данного подхода?

                                        Плюс в том (окей, сознаюсь, сейчас буду про react), что вы один раз декларативно объявляете зависимость вашего внешнего вида от ваших данных и все. Все остальное, генерация vdom, точечные оптимизированные апдейты реального dom за вас делает библиотека.


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


                                        Плюс virtual dom в том, что вы не меняете своей "схемы ui", вы просто "засовываете" в нее новые данные, а vdom уже сам решает, какие узлы dom должны быть изменены.


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


                                        1. Immortal_pony
                                          05.08.2017 06:38

                                          Все запросы асинхронны. То, что вы их выполняете «последовательно», еще не значит что синхронно.

                                          Именно это оно и значит. Если я выполняю свои запросы последовательно — значит синхронно.
                                          Другое дело, что независимые друг от друга запросы я могу выполнять параллельно, то есть асинхронно. Но в данном примере таких запросов нет.

                                          Ну у вас бэк знает как рендерить одни куски UI, а клиент — другие (спиннеры и т.п.).

                                          Нет, клиент ничего не умеет рендерить вообще. Он умеет только показывать отрендеренные заранее куски, анимировать их или заменять их полученными от сервера другими кусками.

                                          Огромное спасибо за разъяснение про схемы работы различных компонентов и технологий с подробными примерами.
                                          У меня появилось понимание, в каких случаях использование JS Framework'ов может быть востребовано.


                                          1. raveclassic
                                            05.08.2017 12:55

                                            Именно это оно и значит.

                                            Ну нет же. Синхронно/асинхронно — это не последовательно/конкурентно (в js нет параллельных вызовов, если только не через воркер, но мы сейчас не об этом). Вызовы (если мы про xhr/fetch) могут быть асинхронными последовательными и асинхронными конкурентными.


                                            //асинхронный последовательный
                                            call1().then(call2).then(call3).then(::console.log);
                                            //так как сначала выведется '1' а затем цепочка
                                            console.log('1)';
                                            
                                            //асинхронный конкурентный
                                            Promise.all([call1(), call2(), call3()]).then(::console.log);
                                            //сначала '2', а заетм коллы, но в неупорядоченном виде
                                            console.log('2');

                                            Нет, клиент ничего не умеет рендерить вообще.

                                            Я даже не знаю, что на это ответить… innerHTML, document.createElement, appendChild и компания?


                                            const html = (name) => `
                                              <div>
                                                OMG ${name} I'm rendered on client side!
                                              </div>
                                            `;
                                            const element = document.createElement('div');
                                            element.innerHTML = html('Carl');
                                            document.body.appendChild(element);

                                            Это самый простой случай, что скажете?


                                            1. Immortal_pony
                                              06.08.2017 00:00

                                              Синхронно/асинхронно — это не последовательно/конкурентно

                                              Это именно последовательно/конкурентно, когда мы говорим про запросы относительно друг друга, а не отсноительно остального кода.


                                              call1().then(call2).then(call3).then(::console.log); // цепочка синхронных (последовательных относительно друга друга) вызовов

                                              Дополню цитатой из словаря:


                                              In computer science, especially parallel computing, synchronization means the coordination of simultaneous threads or processes to complete a task in order to get correct runtime order and avoid unexpected race conditions.

                                              Я даже не знаю, что на это ответить… innerHTML, document.createElement, appendChild и компания?

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


        1. mSnus
          02.08.2017 17:37

          Не забудьте все это в Kubernetes запаковать, а то масштабировать будет неудобно!))


          Вообще вы правы в том, что эти технологии нужны, но неправы в том, что считаете из нужными независимо от масштаба.


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


        1. Trixon
          05.08.2017 10:04

          Какой из вас программист, если вы используя библиотеку, пишите «макаронный код», да ещё и обвиняете в этом саму библиотеку?

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


  1. stardust_kid
    02.08.2017 00:19
    +9

    <p className="user-email"> <SvgMail />&nbsp;&nbsp;{user.email}</p>

    Вы серьезно?


    var xmlHttp = new XMLHttpRequest();

    • window.fetch?
    • Не, не слышал.

    И такого у вас вся статья. Вам бы самому получиться где-то. Там, глядишь, и что такое REST поймете. И только уж после этого начинайте туториалы публиковать. Статье жирный минус.


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


    1. tasty_brains
      02.08.2017 00:55

      Запрос к серверу на каждую иконку?


      1. tasty_brains
        02.08.2017 01:02
        +3

        Пардон, неправильно понял. Цитаты не разделены были.

        window.fetch?
        Не, не слышал.


        Вот тут необоснованно. Технология в некоторых браузерах появилась только в этом году.


        1. stardust_kid
          02.08.2017 01:56
          +4

          А полифилу уже много лет.


          1. Odrin
            02.08.2017 09:57

            Да, но ни тот, ни другой все еще не поддерживают отмену запроса. Мелочь, но из за этого пришлось отказаться от fetch в пользу axios.


            1. raveclassic
              02.08.2017 10:03

              Ну кстати много споров по этому вопросу (отмене). Я как-то до сих пор метаюсь, но последнее время стал-таки склоняться, что запрос, эм… нельзя отменить, он же уже ушел. Можно отменить реакцию на него (как это в rxjs делается), но это уже доп. обертка над этим фетчем.


              1. stardust_kid
                02.08.2017 13:24
                +1

                Ни разу не сталкивался с необходимостью отмены запроса. Или отмены реакции. По-моему, идемпотентность решает эту проблему.


                1. StLukas1
                  02.08.2017 15:46

                  Если перестал быть нужен результат, то зачем передавать его по сети? Особенно для мобильного сайта или PWA.


                1. Aingis
                  03.08.2017 14:10

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


            1. stardust_kid
              02.08.2017 13:15

              Cancellation token пробовали?


    1. bitver
      02.08.2017 07:51
      +2

      Поддерживаю. Тем более XMLHttpRequest не поддерживается в ServiceWorker и значит не для «прогрессивного web-приложения» о котором пишется статье.


    1. Adium
      02.08.2017 09:38
      -5

      А что вы еще ожидали от мылосру? Амиго, мылоспутник, мылогвард и прочие говноподелки ясно дают понять об уровне компании и сидящих там работниках


      1. psFitz
        02.08.2017 11:30
        -2

        Что за бред? У мыла все сервисы работают в 100500 раз быстрее того же гугла, зайдите на те же вопросы и поклацайте на вкладки.


        1. Caravus
          02.08.2017 18:41

          Сравниваем сервера в России с серверами на другом конце земного шара, ага? У гугла даже в СНГ ДЦ нет, насколько я знаю.


  1. mSnus
    02.08.2017 03:22
    +1

    "С помощью AJAX мы можем запрашивать ресурсы из одного одного или нескольких мест (или локально, если страница расположена на том же сервере, что и запрашиваемый URI) и в любое время, без замедления работы веб-приложений или необходимости начать отрисовку всех данных. "


    Серьезно? Прямо таки без замедлений? Ушёл майнить биткоины Аяксом. Или сразу запрошу готовых, из нескольких мест, раз можно.


    Слушайте, ну нельзя так, столько косяков в одной фразе.


    1. tehSLy
      02.08.2017 10:50

      Есть мнение, что фраза была употреблена в контексте «AJAX не фризит страницу на размер лага от запроса, не считая издержек на вызов и все эти подкапотные операции». Не?


  1. mSnus
    02.08.2017 03:25
    +3

    "Разрабатывать… может быть пугающим занятием, если не сказать больше. "


    На скрижаль, однозначно.


  1. DeLuxis
    02.08.2017 06:25
    +1

    Ох уж эти войтивайти… Поскорее бы вас сдуло…


  1. Dekree
    02.08.2017 08:46
    +3

    self.addEventListener('fetch', function(e) {
      e.respondWith(caches.match(e.request)
        .then(function(response) {
          return response || fetch(e.request)
            .then(function (resp){
              return caches.open(cacheName)
                .then(function(cache){
                  cache.put(e.request, resp.clone());
                  return resp;
              })
            }).catch(function(event){
              console.log('Error fetching data!');
            })
          })
      );
    });
    


    Промисы спасут нас от callback hell говорили они…


    1. Odrin
      02.08.2017 10:01
      +3

      Ничто не спасет от криворукости. Этот код без проблем переписывается в плоскую последовательность .then(...), а с async/await вообще красота получится. Но не в случае с «прогрессивным веб-приложением» от mail.ru, конечно.


  1. darkslave
    02.08.2017 09:29
    -2

    Не поверил своим глазам, когда увидел inline svg код для иконок. Мой дилетантский опыт подсказывает, что такое делают через кастомные шрифты, тем более, что автор делает в material-design стиле.


    1. franzose
      02.08.2017 09:41

      В inline SVG в общем-то нет ничего страшного. Немного засоряется вёрстка, конечно, но стилить всякие ховеры и разные состояние становится немного проще. Хотя я в основном пользуюсь defs > symbol > use для этого.


      1. darkslave
        07.08.2017 11:59
        +1

        Если не затруднит, поясните, а в чем профит от инлайн svg? По мне, инлайн svg — это как вставить картинку через data/image. Да, уменьшается кол-во запросов к бекенду и для небольших изображений браузер скорее всего быстрее отрисует область… Но вместе с этим уменьшается гибкость решения, и при желании изменить иконку во всех местах проекта, придется немало покопаться…


        1. franzose
          07.08.2017 14:45

          Проще стилизация состояний. Но не скажу, что я сторонник inline svg. Если иконка присутствует один раз на странице, то еще можно подумать. Использовать svg-спрайты не умею, поэтому вручную делаю defs > symbol + use.


    1. Alexufo
      02.08.2017 15:19
      +2

      От кастомных шрифтов потихоньку отказываются.


  1. xxxTy3uKxxx
    02.08.2017 11:03
    +2

    Забавляют комментаторы про "мылору" и говнопрогрессивное приложение, которые даже не успевают заметить плашку "Перевод" в стремлении написать сообщение


    1. Caravus
      02.08.2017 11:15
      +2

      Если кто-то потратил на перевод своё время (время компании, судя по размещению в её блоге) значит кто-то считает это полезным.


      1. xxxTy3uKxxx
        02.08.2017 11:27

        Но это не отменяет того факта, что глупо спускать всех собак с формулировкой "это же мылору, опять говно впаривают". Стоило сделать комментарии от переводчика, согласен. Если он понимает косяки, допущеные в оригинале, конечно же.


        1. Caravus
          02.08.2017 11:30

          Если бы он понимал косяки — этого перевода бы не было. Зачем переводить материал с косяками когда без косяков полный интернет контента? Так что это взаимоисключающие параграфы.


    1. stardust_kid
      02.08.2017 13:18
      +1

      Ну да это перевод. Разве это отменяет претензии к смыслу?


  1. G-M-A-X
    02.08.2017 11:04

    Вся суть реакта — это возможность писать кашу из html и js при том, что html без кавычек? :)
    Вы из-за этого его используете?


    1. Amareis
      02.08.2017 12:16

      Помнится, кстати, тоже такая мысль проскакивала. Но потом я React+Redux попробовал и могу сказать что на самом деле это действительно удобно: компоненты должны быть маленькими и глупыми (то есть не содержать какого-то сложного JS-кода, не знать ничего про вышестоящий код), так что в них в основном остаётся этот самый HTML и самую чуточку JS. А так как по отдельности этот JS и HTML смысла не имеют, то и лежат они вместе, в одном файле.
      Ну и кроме того JSX использовать не обязательно, это лишь небольшой синтаксический сахар над вызовом одной функции.


      1. i360u
        02.08.2017 13:20

        лишь небольшой синтаксический сахар

        скорее "синтаксическая кинза". Очень на любителя.


    1. stardust_kid
      02.08.2017 13:17
      +1

      У вас, кажется, ПОДГОРАЕТ


      1. G-M-A-X
        05.08.2017 11:33

        Не, мне просто интересно, какие задачи React решает на самом деле :)
        Из-за чего такой хайп.


  1. rboots
    02.08.2017 12:10
    +1

    К чему были разделы про REST и AJAX? Существуют frontend-разработчики, которые могут это не знать? За речью капитана очевидности так и не удалось понять, в чём же отличие Progressive Wep Apps от того, как было принято делать приложения последние годы.


    1. andreysmind
      02.08.2017 13:50

      В том, что кто-то в Гугле придумал новую ПАРАДИГМУ и теперь пиарит на каждом углу.


    1. Pongo
      02.08.2017 14:07
      +1

      PWA можно установить на телефон и запускать как обычные мобильные приложения.