как сделать приложение html
HTML-приложения
HTA-приложения можно писать в любом текстовом редакторе или специализированном HTML-конструкторе. Я, например, пользуюсь программой Notepad2.
Чтобы создать новое приложение, откроем блокнот, скопируем туда структуру HTML документа и сохраним как htm-файл.
Помимо управлением окном приложения, этот тег также дает возможность обращаться к свойствам приложения из скриптов, расположенных в коде HTA-приложения. Стоит заметить, что все свойства этого тега доступны только для чтения.
Содержит имя приложения. Если свойствоSingleInstanceустановлено в «True», то значение ApplicationName автоматически проверяется перед запуском экземпляра приложения. Чтобы проверка была успешной, значение ApplicationName должно быть уникальным. То есть, значение свойства ApplicationName используется для идентификации единственности запущенного приложения.
Содержит тип бордюра окна. Свойство Border влияет на толщину бордюра и действительно только для окон HTA, у которых есть панель заголовка и сам заголовок. УстанавливаяBorderв «None», вы убираете панель заголовка, иконку программы, и кнопки максимизирования и минимизирования. Это свойство может быть использовано совместно со свойством BorderStyle. Возможные значения:
Содержит стиль бордюра клиентской области окна. Свойство BorderStyle устанавливает стиль для бордюра содержимого окна, в то время как свойство Border контролирует бордюр окна приложения. Возможные значения:
Complex | Приподнятый и утопленный бордюр. |
Normal | Нормальный бордюр. (Значение по умолчанию). |
Raised | Приподнятый 3-D бордюр. |
Static | 3-D бордюр, обычно используемый для окон, не обрабатывающих ввод пользователя. |
Sunken | Утопленный 3-D бордюр. |
Определяет, будет ли в окне HTML приложения отображаться панель заголовка. Заголовок приложения отображается только тогда, когда свойствоCaptionустановлено в «Yes». Отключение свойстваCaptionтакже отключит кнопки «Свернуть», «Развернуть» и программную иконку. В этом случае нужно не забыть предоставить альтернативный способ выхода из приложения, например, кнопку «Закрыть» на форме приложения, вызывающую метод Window.Close. Возможные значения:
Yes | Панель заголовка отображается. (Значение по умолчанию). |
No | Панель заголовка не отображается. |
Строка, которая содержит путь и параметры командной строки, которые использовались для запуска HTA-приложения. Если HTA-приложение было запущено с использованием HTTP протокола, свойство CommandLine содержит пустую строку.
Определяет, появляется ли контекстное меню при нажатии на правую кнопку мыши. Возможные значения:
Yes | Контекстное меню появляется. (Значение по умолчанию). |
No | Контекстное меню не появляется. |
Определяет, отображается ли внутренняя 3-D граница. Возможные значения:
Yes | Внутренняя 3-D граница отображается. (Значение по умолчанию). |
No | Внутренняя 3-D граница не отображается. |
Определяет, отображается ли кнопка «Развернуть» на панели заголовка окна HTML приложения. Чтобы отображались кнопки «Свернуть» и «Развернуть», окно должно иметь панель заголовка (атрибут Caption). Возможные значения:
Yes | Кнопка «Развернуть» отображается. (Значение по умолчанию). |
No | Кнопка «Развернуть» не отображается. |
Определяет, отображается ли кнопка «Свернуть» на панели заголовка окна HTML приложения. Чтобы отображались кнопки «Свернуть» и «Развернуть», окно должно иметь панель заголовка (атрибут Caption). Возможные значения:
Yes | Кнопка «Свернуть» отображается. (Значение по умолчанию). |
No | Кнопка «Свернуть» не отображается. |
Определяет, в каком окне будут открываться загружаемые документы. Возможные значения:
No | Будут открываться в новых окнах. (Значение по умолчанию). |
Yes | Будут открываться в основном окне. |
Определяет, будут ли отображаться полосы прокрутки. Возможные значения:
Yes | Полосы прокрутки отображаются. (Значение по умолчанию). |
No | Полосы прокрутки не отображаются. |
Auto | Полосы прокрутки появляются только тогда, когда содержимое документа не умещается в клиентской области окна. |
Определяет, в каком виде будут отображаться полосы прокрутки. Возможные значения:
Yes | Полосы прокрутки двухмерные. |
No | Полосы прокрутки трёхмерные. (Значение по умолчанию). |
Определяет, может ли содержимое документа быть выбрано мышкой или с помощью клавиатуры. Значение «No» запрещает появление контекстного меню и присвоение атрибуту ContextMenu значения «Yes» не произведёт никакого эффекта. Возможные значения:
Yes | Содержимое может быть выбрано. (Значение по умолчанию). |
No | Содержимое не может быть выбрано. |
Определяет, будет ли HTML приложение появляться на панели задач Windows. Возможные значения:
Yes | Приложение отображается на панели задач. (Значение по умолчанию). |
No | Приложение не отображается на панели задач. |
Определяет, может ли быть запущенно больше одного экземпляра HTML приложения одновременно. Возможные значения:
Yes | Может быть запущен только один экземпляр приложения. |
No | Может быть запущенно несколько экземпляров приложения одновременно. (Значение по умолчанию). |
Определяет, отображается ли системное меню в HTML приложении. Системное меню HTA обозначается программной иконкой в левом углу панели заголовка. Системное меню HTA показывает все команды, входящие в стандартное системное меню Windows, включая «Восстановить», «Переместить», «Размер», «Свернуть», «Развернуть», и «Закрыть». Возможные значения:
Yes | Системное меню отображается в панели заголовка. (Значение по умолчанию). |
No | Системное меню не отображается в панели заголовка. |
Определяет номер версии HTML приложения.
Определяет начальные размеры окна HTML приложения. Возможные значения:
Создание Web приложений с HTML 5
Это перевод статьи выложенной на сайте IBM. Автор — Michael Galpin, Software architect, eBay
Обобщение
Введение
Многие возможности и стандарты стали частью HTML 5. После того как вы определите какие функции доступны в сегодняшних браузерах,вы можете использовать эти возможности в вашем приложении. В этой статье вы узнаете как определить эти возможности и использовать современные Веб-технологии путем разработки приложения на примере. Большинство кода в этой статье только HTML, JavaScript, и CSS— это ядро технологии любого Веб-разработчика.
Начало
Для того чтобы следовать примерам самое важное что от вас потребуется — наличие нескольких браузеров для проверки. последнии версии Mozilla Firefox, Apple Safari, и Google Chrome настоятельно рекомендуются. При написании статьи использовались Mozilla Firefox 3.6, Apple Safari 4.04, и Google Chrome 5.0.322. Вы можете так же попробовать протестировать мобильные браузеры. Например, последние Android и iPhone SDKs исопльзовались для тестирования браузеров в их эмуляторах.
Вы можете скачать исходный код используемый в этой статье.
Примеры включают подключение очень маленького back-end компонента написаного на Java™. JDK 1.6.0_17 и использующего Apache Tomcat 6.0.14. Смотрите ссылки в конце статьи для того чтобы скачать эти приложения.
Определение возможностей
Есть старая шутка о том, что веб-разработчики уделяют 20% времени написанию кода и оставшиеся 80% тратят на то чтобы он работал на всех браузерах. Говорить что веб-разработчики привыкли к отличиям браузеров — несколько неверно. С новой волной инноваций в браузерах эта грустаня действительность опять имеет место быть. Возможности поддерживаемые последними и лучшими из браузеров — постоянно меняются.
Листинг 1. Скрипт определения
Огромное число возможностей и стандартов слилось в стандарте HTML 5. Эта статья фокусируется только на некоторых из них. Скрипт в Листинге 1 определяет четыре возможности:
Скрипт начинается с отображения user agent пользовательского браузера. Это (обычно) строка уникального для каждого из браузеров, хотя она может легко быть изменена. Просто повторю, что определение юзерагента будет хорошо для вашего приложения. Следующий шаг — начало определения возможностей. Сначало, проверяем Web workers через поиск функции Worker в глобальной области видимости (окно|window). Для этого используем одну идиому из JavaScript: double negation (двойное отрицание). Если функция Worker не существует, то window.Worker будет неопределенной, что будет значением «ложь» в JavaScript. Ввод одинарного отрицания перед ним превратит его в true, в вдойное отрицание превратит в false. После тестирования этого значения скрипт преобразует значение DOM структуры показанной в Листинге 2 для отображения результата теста.
Листинг 2. Определение DOM
Все популярные браузеры для настольных компьютеров поддерживают возможности в большей или меньшей степени:
Geolocation не поддерживает широко браузерами используемыми на настольных компьютерах, однако хорошо поддерживается в мобильных браузерах. Листинг 4 показывает список собранных данных для мобильных браузеров.
Листинг 4. Мобильные браузеры
Выше показаны данные для одного из последних симуляторов iPhone и двух вариантов для Android. Android 1.6 не поддерживает ничего из того что мы тестировали. По факту все это имеет место быть в нем, кроме воспроизведения видео, так как он использует Google Gears. Они эквивалентны API (через функцию), но не соблюдают Веб-стандарты которые мы используем для получения результата в Листинге 4. Сравните с Android 2.1, в нем все поддерживается правильно.
Обратите внимание, что iPhone поддерживает все кроме Web workers. Листинг 3 показывает, что настольная версия Safari поддерживает Web workers, так что есть резон не исключать вероятность появления в скором будущем такой возможности и на iPhone.
Теперь вы научились определять возможности пользовательского браузера, давайте изучим простое приложение которое использует несколько этих возможностей в комбинации—в зависимости от того что может пользовательский браузер. Вы создадите приложение используя Foursquare API для поиска нескольких популярных мест местоположения пользователя.
Создай приложение сегодня
В примере мы фокусируемся на использовании geolocation, но так же имейте в виду что Firefox 3.5+ так же поддерживает geolocation. Приложение начинает поиск звонков Foursquare вблизи текущего местоположения пользователя. Местоположение может быть любым, но обычно это рестораны, бары, магазины или что-то подобное. Будучи веб-приложением наш пример ограничен той же политикой ограничения свойственной всем браузерам. Оно не может вызывать Foursquare’s API напрямую. Вы будете использовать Java servlet как прокси для этих вызовов. В сервлете нет ничего специфичного для Java; вы можете легко переписать этот прокси на PHP, Python, Ruby, и даже на forth. Листинг 5 показывает прокси сервлет.
Листинг 5. Прокси сервлет для Foursquare
Важно отметить, что у вас проксифицирутеся два Foursquare API. Один для поиска, другой для получения детальной информации о месте нахождения. Чтобы их различать в API добавлен оперативный параметр. Вы так же можете указать JSON в качестве возвращаемого типа, для того чтобы позже легко разобрать данные используя JavaScript. Теперь, когда вы знаете какие вызовы совершает приложение и как оно получает данные, давайте посмотрим как оно будет их использовать для получения данных из Foursquare.
Использование geolocation
Это первый вызов в поиске. Листинг 5 показывает что вам требуется два параметра: geoLat и geoLong для широты и долготы. Листинг 6 ниже показывает как получить эти данные и совершить вызов сервлета.
Листинг 6. Вызов поиска с местоположением
Структурированное хранилище
Для сохранения данных о ТЦ в базу данных сначало созхдадим таблицу, в которой мы хотим сохранить данные. Для этого используется довольно стандартный синтаксис SQL для создания таблицы. (Все браузеры поддерживающие базы данных используют SQLite. Используйте документацию SQLite для изучения поддерживаемых, ограничиваемых и т.п. типах данных.) SQL выполняется асинхронно. Вызывается функция передачи данных и получаем данные. Функция callback получает переданный объект который мы можем использовать для выполнения SQL. Функция executeSQL принимает строку SQL, затем опциональные параметры, полюс правильность выполнения или функцию обработки ошибки. Если обработчика ошибок нет, то и ошибок нет. Для create table это хорошо. При первом запуске сценария таблица будет создана. В последующие запуски скрипт не сможет создать таблицу, так как таблица уже существует— но это правильно. вам это требуется просто для того, чтобы убедится что таблица создана, до того как будете делать вставку данных в нее.
После создания таблицы используем функцию forEach для вызова функции saveVenue с возвратом каждого торгового центра из Foursquare. Эта функция сначало делает запрос в БД чтобы убедится что такие данные уже не хранятся локально. Здесь вы видите исполнение обработчика. Результат полученный в запросе передается в обработчик. Если результатов нет, или ТЦ еще не был сохранен локально то вызывается функция insertVenue котора вставляет данные в БД.
После того как вы изучили использование БД если она поддерживается — следующий шаг — изучить работу Web worker если они поддерживаются.
Фоновые процессы с использованием Web workers
Вернемся к Листингу 6, и внесем в него изменения. Как показано ниже в Листинге 9, проверим браузер на поддержку Web worker. Если они поддерживаются — получим с их помощью больше информации о ТЦ.
Listing 9. Измененный поиск ТЦ
Код выше использует тоже определение возможностей что вы видели до этого. Если Web workers поддерживаются, то вы создаете нового worker. Для создания нового worker, вам требуется URL на другой скрипт, который worker будет выполнять, к примеру — details.js. Когда worker завершает работу, отправляет сообшение обратно в основной поток. Обработчик onmessage получает это сообщение; используйте простое его закрытие. Наконец, чтобы инициировать worker — вызовите postMessage с передачей некоторых данных для обработки. Вы передадите все данные полученные из Foursquare. Листинг 10 показывает соджержимое details.js, который является скриптом worker’ом.
Listing 10. Worker скрипт — details.js
По умолчанию поиск торговых центров возвращает 10 ТЦ. Вы можете себе представить сколько вызовов совершается чтобы получить детальные данные о каждом из них? Но Web workers делают задачу легкой и незаметной для пользователя, делая свою работу в фоновом режиме.
В этой статье освещено несколько новых возможностей HTML 5 поддерживаемых современными браузерами. Вы научились определять какие возможности поддерживаются браузерами и как прогрессивно наращивать мощь своего Веб-приложения, используя эти возможности. Большинство функционала уже широко поддерживается даже на мобильных браузерах. теперь вы можете начать исопльзовать такие в высшей степени примечательные вещи как geolocation и Web workers для создания новых, креативных Веб-приложений!
Исходный код статьи | FutureWeb.zip | 9KB | HTTP |
Источники
Обучение
Получение продуктов и технологий
Делаем современное веб-приложение с нуля
Итак, вы решили сделать новый проект. И проект этот — веб-приложение. Сколько времени уйдёт на создание базового прототипа? Насколько это сложно? Что должен уже со старта уметь современный веб-сайт?
В этой статье мы попробуем набросать boilerplate простейшего веб-приложения со следующей архитектурой:
Введение
Перед разработкой, конечно, сперва нужно определиться, что мы разрабатываем! В качестве модельного приложения для этой статьи я решил сделать примитивный wiki-движок. У нас будут карточки, оформленные в Markdown; их можно будет смотреть и (когда-нибудь в будущем) предлагать правки. Всё это мы оформим в виде одностраничного приложения с server-side rendering (что совершенно необходимо для индексации наших будущих терабайт контента).
Давайте чуть подробнее пройдёмся по компонентам, которые нам для этого понадобятся:
Инфраструктура: git
Наверное, про это можно было и не говорить, но, конечно, мы будем вести разработку в git-репозитории.
Итоговый проект можно посмотреть на Github. Каждой секции статьи соответствует один коммит (я немало ребейзил, чтобы добиться этого!).
Инфраструктура: docker-compose
Начнём с настройки окружения. При том изобилии компонент, которое у нас имеется, весьма логичным решением для разработки будет использование docker-compose.
Добавим в репозиторий файл docker-compose.yml следующего содержания:
Давайте разберём вкратце, что тут происходит.
Не менее важный docker/backend/.dockerignore :
Воркер в целом аналогичен бэкенду, только вместо gunicorn у нас обычный запуск питонячьего модуля:
Наконец, фронтенд. Про него на Хабре есть целая отдельная статья, но, судя по развернутой дискуссии на StackOverflow и комментариям в духе «Ребят, уже 2018, нормального решения всё ещё нет?» там всё не так просто. Я остановился на таком варианте докерфайла.
Итак, наш каркас из контейнеров готов и можно наполнять его содержимым!
Бэкенд: каркас на Flask
Теперь наш бэкенд мы можем официально ПОДНЯТЬ!
Фронтенд: каркас на Express
В дальнейшем нам вообще не потребуется Node.js на машине разработчика (хотя мы могли и сейчас извернуться и запустить npm init через Docker, ну да ладно).
В Dockerfile мы упомянули npm run build и npm run start — нужно добавить в package.json соответствующие команды:
Команда build пока ничего не делает, но она нам ещё пригодится.
Добавим в зависимости Express и создадим в index.js простое приложение:
Теперь docker-compose up frontend поднимает наш фронтенд! Более того, на http://localhost:40002 уже должно красоваться классическое “Hello, world”.
Фронтенд: сборка с webpack и React-приложение
Пришло время изобразить в нашем приложении нечто больше, чем plain text. В этой секции мы добавим простейший React-компонент App и настроим сборку.
При программировании на React очень удобно использовать JSX — диалект JavaScript, расширенный синтаксическими конструкциями вида
Однако, JavaScript-движки не понимают его, поэтому обычно во фронтенд добавляется этап сборки. Специальные компиляторы JavaScript (ага-ага) превращают синтаксический сахар в уродливый классический JavaScript, обрабатывают импорты, минифицируют и так далее.
2014 год. apt-cache search java
Итак, простейший React-компонент выглядит очень просто.
Он просто выведет на экран наше приветствие более убедительным кеглем.
Добавим и клиентскую точку входа:
Для сборки всей этой красоты нам потребуются:
webpack — модный молодёжный сборщик для JS (хотя я уже три часа не читал статей по фронтенду, так что насчёт моды не уверен);
babel — компилятор для всевозможных примочек вроде JSX, а заодно поставщик полифиллов на все случаи IE.
Если предыдущая итерация фронтенда у вас всё ещё запущена, вам достаточно сделать
для установки новых зависимостей. Теперь настроим webpack:
Чтобы заработал babel, нужно сконфигурировать frontend/.babelrc :
Наконец, сделаем осмысленной нашу команду npm run build :
Бэкенд: данные в MongoDB
Прежде, чем двигаться дальше и вдыхать в наше приложение жизнь, надо сперва её вдохнуть в бэкенд. Кажется, мы собирались хранить размеченные в Markdown карточки — пора это сделать.
В то время, как существуют ORM для MongoDB на питоне, я считаю использование ORM практикой порочной и оставляю изучение соответствующих решений на ваше усмотрение. Вместо этого сделаем простенький класс для карточки и сопутствующий DAO:
(Если вы до сих пор не используете аннотации типов в Python, обязательно гляньте эти статьи!)
Теперь надо создать MongoCardDAO и дать Flask-приложению к нему доступ. Хотя сейчас у нас очень простая иерархия объектов (настройки → клиент pymongo → база данных pymongo → MongoCardDAO ), давайте сразу создадим централизованный царь-компонент, делающий dependency injection (он пригодится нам снова, когда мы будем делать воркер и tools).
Время добавить новый роут в Flask-приложение и наслаждаться видом!
Упс… ох, точно. Нам же нужно добавить контент! Заведём папку tools и сложим в неё скриптик, добавляющий одну тестовую карточку:
Успех! Теперь время поддержать это на фронтенде.
Фронтенд: Redux
Начнём с добавления Redux. Redux — JavaScript-библиотека для хранения состояния. Идея в том, чтобы вместо тысячи неявных состояний, изменяемых вашими компонентами при пользовательских действиях и других интересных событиях, иметь одно централизованное состояние, а любое изменение его производить через централизованный механизм действий. Так, если раньше для навигации мы сперва включали гифку загрузки, потом делали запрос через AJAX и, наконец, в success-коллбеке прописывали обновление нужных частей страницы, то в Redux-парадигме нам предлагается отправить действие “изменить контент на гифку с анимацией”, которое изменит глобальное состояние так, что одна из ваших компонент выкинет прежний контент и поставит анимацию, потом сделать запрос, а в его success-коллбеке отправить ещё одно действие, “изменить контент на подгруженный”. В общем, сейчас мы это сами увидим.
Начнём с установки новых зависимостей в наш контейнер.
Первое — собственно, Redux, второе — специальная библиотека для скрещивания React и Redux (written by mating experts), третье — очень нужная штука, необходимость который неплохо обоснована в её же README, и, наконец, четвёртое — библиотечка, необходимая для работы Redux DevTools Extension.
Начнём с бойлерплейтного Redux-кода: создания редьюсера, который ничего не делает, и инициализации состояния.
Наш клиент немного видоизменяется, морально готовясь к работе с Redux:
Фронтенд: страница карточки
Прежде, чем сделать страницы с SSR, надо сделать страницы без SSR! Давайте наконец воспользуемся нашим гениальным API для доступа к карточкам и сверстаем страницу карточки на фронтенде.
Время воспользоваться интеллектом и задизайнить структуру нашего состояния. Материалов на эту тему довольно много, так что предлагаю интеллектом не злоупотреблять и остановится на простом. Например, таком:
Заведём компонент «карточка», принимающий в качестве props содержимое cardData (оно же — фактически содержимое нашей карточки в mongo):
Теперь заведём компонент для всей страницы с карточкой. Он будет ответственен за то, чтобы достать нужные данные из API и передать их в Card. А фетчинг данных мы сделаем React-Redux way.
Для начала создадим файлик frontend/src/redux/actions.js и создадим действие, которые достаёт из API содержимое карточки, если ещё не:
Ох, у нас появилось действие, которое ЧТО-ТО ДЕЛАЕТ! Это надо поддержать в редьюсере:
(Обратите внимание на сверхмодный синтаксис для клонирования объекта с изменением отдельных полей.)
Теперь, когда вся логика унесена в Redux actions, сама компонента CardPage будет выглядеть сравнительно просто:
Добавим простенькую обработку page.type в наш корневой компонент App:
И теперь остался последний момент — надо как-то инициализировать page.type и page.cardSlug в зависимости от URL страницы.
Но в этой статье ещё много разделов, мы же не можем сделать качественное решение прямо сейчас. Давайте пока что сделаем это как-нибудь глупо. Вот прям совсем глупо. Например, регуляркой при инициализации приложения!
Так, секундочку… а где же наш контент? Ох, да мы ведь забыли распарсить Markdown!
Воркер: RQ
Парсинг Markdown и генерация HTML для карточки потенциально неограниченного размера — типичная «тяжёлая» задача, которую вместо того, чтобы решать прямо на бэкенде при сохранении изменений, обычно ставят в очередь и исполняют на отдельных машинах — воркерах.
Есть много опенсорсных реализаций очередей задач; мы возьмём Redis и простенькую библиотечку RQ (Redis Queue), которая передаёт параметры задач в формате pickle и сама организует нам спаунинг процессов для их обработки.
Время добавить редис в зависимости, настройки и вайринг!
Немного бойлерплейтного кода для воркера.
Для самого парсинга подключим библиотечку mistune и напишем простенькую функцию:
Мы объявили свой класс джобы, прокидывающий вайринг в качестве дополнительного kwargs-аргумента во все таски. (Обратите внимание, что он создаёт каждый раз НОВЫЙ вайринг, потому что некоторые клиенты нельзя создавать перед форком, который происходит внутри RQ перед началом обработки задачи.) Чтобы все наши таски не стали зависеть от вайринга — то есть от ВСЕХ наших объектов, — давайте сделаем декоратор, который будет доставать из вайринга только нужное:
Добавляем декоратор к нашей таске и радуемся жизни:
Радуемся жизни? Тьфу, я хотел сказать, запускаем воркер:
Ииии… он ничего не делает! Конечно, ведь мы не ставили ни одной таски!
Давайте перепишем нашу тулзу, которая создаёт тестовую карточку, чтобы она: а) не падала, если карточка уже создана (как в нашем случае); б) ставила таску на парсинг маркдауна.
Пересобрав контейнер с бэкендом, мы наконец можем увидеть контент нашей карточки в браузере:
Фронтенд: навигация
Прежде, чем мы перейдём к SSR, нам нужно сделать всю нашу возню с React хоть сколько-то осмысленной и сделать наше single page application действительно single page. Давайте обновим нашу тулзу, чтобы создавалось две (НЕ ОДНА, А ДВЕ! МАМА, Я ТЕПЕРЬ БИГ ДАТА ДЕВЕЛОПЕР!) карточки, ссылающиеся друг на друга, и потом займёмся навигацией между ними.
Теперь мы можем ходить по ссылкам и созерцать, как каждый раз наше чудесное приложение перезагружается. Хватит это терпеть!
Сперва навесим свой обработчик на клики по ссылкам. Поскольку HTML со ссылками у нас приходит с бэкенда, а приложение у нас на React, потребуется небольшой React-специфический фокус.
Добавляем глупенький редьюсер под это дело:
Внимательный читатель обратит внимание, что URL страницы не будет изменяться при навигации между карточками — даже на скриншоте мы видим Hello, world-карточку по адресу demo-карточки. Соответственно, навигация вперёд-назад тоже отвалилась. Давайте сразу добавим немного чёрной магии с history, чтобы починить это!
Теперь при переходах по ссылкам URL в адресной строке браузера будет реально меняться. Однако, кнопка «Назад» сломается!
А вот как — действие navigate:
Вот теперь история заработает.
Фронтенд: server-side rendering
Пришло время для нашей главной (на мой взгляд) фишечки — SEO-дружелюбия. Чтобы поисковики могли индексировать наш контент, полностью создаваемый динамически в React-компонентах, нам нужно уметь выдавать им результат рендеринга React, и ещё и научиться потом делать этот результат снова интерактивным.