# Написание универсального кода
Прежде чем продолжить, давайте обсудим ограничения при написании «универсального» кода — т.е. кода, который работает как на сервере, так и на клиенте. Из-за различий в сценариях использования и API платформ поведение кода не будет абсолютно одинаковым при работе в разных окружениях. В этом разделе рассмотрены лишь основные моменты, о которых следует помнить.
# Реактивность данных на сервере
В приложениях, работающих только на стороне клиента, каждый пользователь использует свежий экземпляр приложения в своём браузере. Для отрисовки на стороне сервера может захотеться того же: чтобы каждый запрос получал свежий, изолированный экземпляр приложения и не происходило загрязнения состояния между запросами.
Поскольку фактический процесс отрисовки должен быть детерминированным, данные на сервере также будут «предварительно загружаться» — это означает, что состояние приложения уже будет определено, когда начнётся отрисовка. Также это означает, что на сервере реактивность данных не нужна, поэтому она отключена по умолчанию. Кроме того отключение реактивности помогает избежать затрат производительности на преобразование данных в реактивные объекты.
# Хуки жизненного цикла компонента
Так как динамических обновлений не будет, то во время SSR вызываются хуки жизненного цикла beforeCreate
и created
. Это означает, что любой код в других хуках жизненного цикла, например beforeMount
или mounted
, будет выполняться только на клиенте.
Ещё одно замечание: следует избегать кода в beforeCreate
или created
, который создаёт глобальные побочные эффекты, например, устанавливая таймеры с помощью setInterval
. В клиентском коде можно установить таймер, а затем уничтожить его в beforeUnmount
или unmounted
. Но так как хуки уничтожения не вызываются во время SSR, таймеры останутся навсегда. Избежать такого можно переносом кода побочного эффекта в beforeMount
или mounted
.
# Доступ к API, специфичным для конкретной платформы
Универсальный код не может предлагать доступ к API, специфическим для платформы, поэтому если в коде напрямую используются глобальные значения только для браузера, такие как window
или document
, то при выполнении в Node.js они будут вызывать ошибки, и наоборот.
Для задач, общих для сервера и клиента, но использующих различное API платформ, рекомендуется оборачивать специфические для платформы реализации в универсальный API или использовать библиотеки, которые выполняют это самостоятельно. Например, axios (opens new window) — HTTP-клиент, который предоставляет одинаковый API как для сервера, так и клиента.
Для браузерных API распространённым подходом является ленивый доступ к ним внутри хуков жизненного цикла, выполняющихся только на клиенте.
Обратите внимание, что если сторонняя библиотека не написана с учётом универсального использования, то может быть проблемой интегрировать её в приложение с отрисовкой на стороне сервера. Конечно можно попробовать заставить её работать, создавая моки для некоторых глобальных свойств, но это будет грязным хаком и может помешать коду для определения окружения в других библиотеках.
# Пользовательские директивы
Большинство пользовательских директив непосредственно манипулируют DOM, поэтому будут вызывать ошибки во время SSR. Рекомендуем предпочитать компоненты в качестве механизма абстракции, вместо директив.