Не все сторонние компоненты поддерживают рендеринг на стороне сервера (SSR). Nuxt.js предоставляет вам способ исключить эти компоненты из фазы рендеринга на стороне сервера, что является тегом ‹client-only› или ‹no-ssr› (устаревший).

<template>
  <div>
    <client-only>
      <carousel v-bind:perPage="2">
        <slide 
          v-for="imgUrl in imgUrls" 
          v-bind:key="imgUrl">
            <img v-bind:src="imgUrl">
        </slide>
      </carousel>
    </client-only>
  </div>
</template>

Когда вы это сделаете, все содержимое тега ‹client-only› не будет отображаться во время SSR. Это содержимое будет отображаться на стороне клиента после инициализации экземпляра Vue в браузере пользователя. В качестве передовой практики следует принять решение, которое следует учитывать при использовании тега ‹client-only›: Важна ли внутренняя информация для SEO?

Если контент важен для SEO

Большинство сканеров поисковых систем не запускают JS при сканировании. (Насколько я знаю, JS будет выполнять только Google.) В результате экземпляр Vue никогда не инициализируется, и это содержимое не будет видно сканерам. Чтобы справиться с этим, вы должны добавить заполнители, которые отображаются перед визуализацией этого клиентского содержимого. Экземпляр Vue заменит заполнитель содержимым только для клиента после их загрузки.

<template>
  <div>
    <client-only>
      <carousel v-bind:perPage="2">
        <slide 
          v-for="imgUrl in imgUrls" 
          v-bind:key="imgUrl">
            <img v-bind:src="imgUrl">
        </slide>
      </carousel>
      <template slot="placeholder">
        <img 
          v-for="imgUrl in imgUrls"
          v-bind:key="imgUrl"
          v-bind:src="imgUrl">
      </template>
    </client-only>
  </div>
</template>

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

<template>
  <div>
    <client-only>
      <carousel v-bind:perPage="2">
        <slide 
          v-for="imgUrl in imgUrls" 
          v-bind:key="imgUrl">
            <img v-bind:src="imgUrl">
        </slide>
      </carousel>
      <template slot="placeholder">
        <div class="row">
          <div class="col-6" v-for="imgUrl in imgUrls">
            <img 
              v-bind:key="imgUrl"
              v-bind:src="imgUrl">
          </div>   
        </div>
      </template>
    </client-only>
  </div>
</template>

Это подойдет для большинства случаев, если только ваше клиентское содержимое не загружается лениво. Важно помнить, что заполнитель - это не ‹noscript›. Независимо от того, включен JS или нет, заполнитель всегда будет загружен. Следовательно, если вы реализуете ленивую загрузку в своем клиентском содержимом, вы также должны реализовать ленивую загрузку для своего заполнителя.

Если контент не важен для SEO

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

export default {
  asyncData(context) {
    ...
    return {
      ...
      isReady: false,
      imgUrls: []
    }
  },
  
  mounted() {
    apiToLoadData().then(data => {
      this.isReady = true;
      this.imgUrls = data;
    }
  },
  
  ...
}

Фактически, в этом случае вам даже не понадобится тег ‹client-only›, поскольку ваша клиентская часть никогда не будет готова и в любом случае не будет отображаться на стороне сервера.

<template>
  <div>
    <div v-if="isReady">
      <carousel v-bind:perPage="2">
        <slide 
          v-for="imgUrl in imgUrls" 
          v-bind:key="imgUrl">
            <img v-bind:src="imgUrl">
        </slide>
      </carousel>
    </div>
    <LoadingSpinner v-else />
  </div>
</template>

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