SSR: когда, зачем и для чего. На примере Vue

от автора

Once upon a time Несколько лет назад, когда я только начинал работать с вебом на Java, мы работали с JSP. Вся страница генерировалась на сервере и отправлялась клиенту. Но потом встал вопрос о том, что ответ приходит слишком долго…

Мы начали использовать подход, при котором отдается пустой темплейт страницы, а все данные уже постепенно подгружались Аяксом. Все были счастливы, странички показывались. Пока мы не поняли, что наделали себе за шиворот, так как CSR отрицательно сказывается на поисковой оптимизации и производительности на мобильных устройствах. Но потом я снова услышал про поддержку SSR JS-фреймворками.

И что же получается, история повторяется?

Какие есть принципы работы SSR?

1. Prerendering. В простейшем случае генерируется N HTML-файлов, которые кладутся на сервер и возвращаются как есть — то есть возвращается статика, во время запроса мы ничего не генерируем.

2. Как и в случае с JSP, на сервере генерируется полный HTML со всем контентом и возвращается клиенту. Но, чтобы не генерировать страницу на каждый запрос (коих может быть миллион и наш сервер загнется), давайте добавим кэш прокси. Например, варниш.

Когда может быть применим каждый из этих способов:

1. Когда имеет смысл генерировать пачку HTML-файлов? Очевидно, в том случае, когда данные на сайте меняются чуть реже чем никогда. Например, корпоративный сайт ларька по ремонту обуви, что за углом (да-да, тот дяденька, который меняет набойки в ларьке 2х2 метра, тоже захотел сайт фирмы — и, конечно же, со страницей миссии компании). Для такого сайта вообще не надо заморачиваться на предмет фреймворков, SSR и прочих свистелок, но это сферический пример. Что делать, если у нас блог, в котором 1к постов? Иногда мы их актуализируем, иногда добавляем новые. Сгенерировать 1к+ статичных файлов… Что-то не то. А если мы изменяем пост, то надо перегенерировать определенный файлик. Хм…

2. И вот тут нам подходит второй способ. Где мы генерируем первый раз на лету, а потом кэшируем ответ в проксирующем сервисе. Время кэширования может быть час/два/день — как угодно. Если у нас 10 000 заходов в час на пост (невероятно, правда?), то только первый запрос дойдет до сервера. Остальные получат в ответ кэшированную копию, и наш сервер с большей вероятностью будет жить. В случае обновления какого-то поста нам просто нужно сбросить закэшированную запись, чтобы по следующему реквесту сгенерировалась уже актуальная страница.

От слов к делу:

Hello world repo.

0) generate hello world

Для быстрого старта сообщество Nuxt подготовило базовые темплейты, установить любой из них можно командой:

$ vue init <template-name> <project-name> 

По умолчанию предлагается started-template, его и возьмем для нашего примера. Хотя в реальном приложении мы выбрали express-template. Назовем проект незамысловато:

$ vue init nuxt-community/starter-template habr-nuxt-example $ cd habr-nuxt-example $ yarn # или npm install, как будет угодно $ yarn dev 

Вжух, мы сгенерировали hello world. Перейдя по урлу, можно увидеть сгенерированную страницу:
1) Webpack и Linting

Nuxt из коробки имеет настроенные вебпак с поддержкой ES6 (babel-loader), Vue однофайловые компоненты (vue-loader), а также SCSS, JSX и прочее.

Если этих возможностей недостаточно, конфиг вебпака можно расширить. Идем в nuxt.config.js, и в build.extend мы имеем возможность модифицировать конфиг.

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

Пример расширения конфига (подключение конфиг-файла для дева на основе переменной окружения):

config.plugins.push(  new StylelintPlugin({    files: [      '**/*.vue',      'assets/scss/**/*.scss'    ],    configFile: './.stylelintrc.dev.js'  }) ) 

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

И пример конфиг-файла линтера: используем Standard JS, как общепринятое в Vue/Nuxt решение:

...  extends: [ -    'plugin:vue/essential' +    'standard', +    'plugin:vue/recommended'  ], … 

2) Для примера работы с данными будем использовать вот это API:

Подключим Axios как плагин, создаем новый файл в директории plugins:

import * as axios from 'axios'  let options = {} // The server-side needs a full url to works if (process.server) {  options.baseURL = `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}` }  export default axios.create(options)  

И пример использования:

import axios from '~/plugins/axios'  export default {  async asyncData ({ params }) {    const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')     return { data }  } }  

Остальное в репе по тегу.

Цифры загрузки:
1) SSR + Varnish

Первый запрос:

Второй:

И его хедеры
2) No-ssr

Второй реквест с фастли

Пустая страница пришла быстро, но потребовалось 2 секунды на то, чтобы сгенерировать на ней контент.

Conclusion

Что в итоге? Мы разобрались, как получить минимально сконфигурированное запускаемое SSR-приложение. Добавили Linting для сохранения стиля кода с самого начала жизни проекта, а также обозначили общую архитектуру. Можно писать свой гугол.


ссылка на оригинал статьи https://habr.com/post/425053/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *