Русский
Русский
English
Статистика
Реклама

Nuxtjs

Проблемы рендера 7-и тысяч элементов на Vuetify

03.06.2021 08:11:45 | Автор: admin

Предисловие

На момент написания статьи я готовился к диплому и писал дипломный проект для нужд Московского Политеха. Моя задача - перенести существующий функционал из PHP-таблицы во что-то современное с кучей проверок, после чего дополнить данный функционал. Движок - Nuxt, материал-фреймворк: Vuetify.

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

Первые попытки

Что делает разработчик, когда сталкивается с проблемой? Идет гуглить. Это было первое, что я сделал. Как оказалось, проблема медленного рендера таблицы Vuetify встречается с куда меньшим числом элементов, чем у меня. Что советуют:

  • Рендерить элементы по частям через setInterval

  • Ставить условие, чтобы не рендерить элементы, пока не сработает хук жизненного цикла mounted()

  • Использовать v-lazy для последовательной отрисовки

При этом было предложение использовать компонент Virtual Scroller, позволяющий отрисовывать элементы по мере скролла, а предыдущие разрендеривать. Но этот компонент Vuetify не работает с таблицами Vuetify -_-

С "радостью" прочитав, что в Vuetify 3 (релиз через ~полгода) производительность улучшится на 50%, я стал пробовать решения. Рендер элементов по частям ничего не дал, так как на условном тысячном элементе отрисовка начинала лагать, а к семи тысячам снова всё висло. Рендер элементов на mounted не дал вообще ничего, всё зависало, но зато после того, как страница загрузится (эээ, ура?). v-lazy хоть и рендерился быстрее, но рендерить 14 тысяч компонентов (Vuetify и Transition от Vue) тоже грустное занятие.

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

ТаблицаТаблица

Решение 1. Intersection Observer

Итак, что мы имеем. v-lazy отрисовывать невозможно, это 14 тысяч компонентов. Vuetify Virtual Scroller не поддерживается в Vuetify Data Table из-за его структуры. Выходит, нужно писать свою реализацию. Кто умеет определять, докрутил ли пользователь до элемента? Intersection Observer. Internet Explorer нам не нужен, так что можем приступать.

Первая логичная попытка: использовать директиву v-intersect от самого Vuetify. И 7 тысяч директив также привели к длительному рендеру страницы =(. Значит, выбора нет и придется работать руками.

mounted() {  //Цепляем его на таблицу с overflow: auto  //Требуемая видимость элемента для триггера: 10%this.observer = new IntersectionObserver(this.handleObserve, { root: this.$refs.table as any, threshold: 0.1 });//Почему нельзя повесить observe на все нужные элементы? Ну ладноfor (const element of Array.from(document.querySelectorAll('#intersectionElement'))) {this.observer.observe(element);}},

Теперь взглянем на сам handleObserve:

async handleObserve(entries: IntersectionObserverEntry[]) {const parsedEntries = entries.map(entry => {const target = entry.target as HTMLElement;  //Предварительно задали data-атрибутыconst project = +(target.dataset.projectId || '0');const speciality = +(target.dataset.specialityId || '0');return {          isIntersecting: entry.isIntersecting,          project,          speciality,};    });//Чтобы точно было реактивно    this.$set(this, 'observing', [      //Не добавляем дубликаты      ...parsedEntries.filter(x => x.isIntersecting && !this.observing.some(y => y.project === x.project && y.speciality === x.speciality)),      //Убираем пропавшие      ...this.observing.filter(entry => !parsedEntries.some(x => !x.isIntersecting && x.project === entry.project && x.speciality === entry.speciality)),     ]);//Иначе функция стриггерится несколько раз     Array.from(document.querySelectorAll('#intersectionElement')).forEach((target) => this.observer?.unobserve(target));     //Даем Vuetify перерендериться await this.$nextTick(); //Ждем 300мс, чтобы не триггернуть лишний раз, отрисовка тоже грузит браузер     await new Promise((resolve) => setTimeout(resolve, 500));     //Вновь обсервим элементы     Array.from(document.querySelectorAll('#intersectionElement'))          .forEach((target) => this.observer?.observe(target));},

Итак, мы имеем 7 тысяч элементов, на которых смотрит наш Intersection Observer. Есть переменная observing, в которой содержатся все элементы с projectId и specialityId, по которым мы можем определять, нужно ли показывать нужный элемент в таблице. Осталось всего-лишь повесить v-if на нужный нам элемент и отрисовывать вместо него какую-нибудь заглушку. Ура!

 <template #[`item.speciality-${speciality.id}`]="{item, headers}" v-for="speciality in getSpecialities()"><div id="intersectionElement" :data-project-id="item.id" :data-speciality-id="speciality.id"><ranking-projects-table-itemv-if="observing.some(x => x.project === item.id && x.speciality === speciality.id)":speciality="speciality":project="item"/><template v-else>Загрузка...</template></div></template>

А на саму таблицу вешаем v-once. Таблице будет запрещено менять свой рендер без $forceUpdate. Не очень красивое решение, но Vuetify непонятно чем занимается при скролле, запретим ему это делать.

<v-data-table v-bind="getTableSettings()"   v-once   :items="projects"   @update:expanded="$forceUpdate()">

Итоги:

  • Рендер занимает около секунды

  • Элементы разрендериваются вне их зоны видимости

  • Идет ререндер на каждое действие внутри таблицы

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

Загрузка...Загрузка...

Однако было ясно (и заказчик это подчеркнул), что работать с таблицей тяжело. Нужно было что-то решать, но на момент тогда я зашел в тупик: Vuetify Data Table имеет ужасные проблемы с производительностью, которые признают даже разработчики фреймворка.

Не переписывать же мне их таблицу и создавать свой аналог?

Решение 2. Переписать таблицу и создать свой аналог

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

Что мы поняли из первого решения:

  1. Таблицы Vuetify лагучий отстой

  2. Если показывать элементы только когда пользователь их увидит, рендер будет быстрее

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

  4. Можно заставить Intersection Observer реагировать чаще, чтобы рендерить элементы по мере скролла, а не когда скролл остановится или среагирует событие (300мс задержка)

Возвращаемся к Virtual Scroller. Он не работает в таблице Vuetify, так? А что если мы напишем свою таблицу с блекджеком и display: grid? Зачем-то же его придумали.

Что нужно для Virtual Scroller? Фиксированная высота каждого элемента. Что нужно для Grid'ов? Фиксированная ширина каждого элемента и информация о количестве элементов. Бахнем CSS-переменные для последующего использования в CSS:

<div class="ranking-table" :style="{    '--projects-count': getSettings().projectsCount,    '--specialities-count': getSettings().specialitiesCount,    '--first-column-width': `${getSettings().firstColumnWidth}px`,    '--others-columns-width': `${getSettings().othersColumnsWidth}px`,    '--cell-width': `${getSettings().firstColumnWidth + getSettings().othersColumnsWidth * getSettings().specialitiesCount}px`,    '--item-height': `${getSettings().itemHeight}px`  }">

Потом пишем гриды по типу аля

display: grid;grid-template-columns:var(--first-column-width)repeat(var(--specialities-count), var(--others-columns-width));

И так далее для элементов внутри. Класс! А еще мы, зная ширину и количество элементов, можем задать ширину нашему Virtual Scroller (он сам по умолчанию туповат), а заодно и всем нашим элементам, чтобы не дай боже кто-то вышел за доступные ему границы

.ranking-table_v2__scroll::v-deep {.v-virtual-scroll {&__container, &__item, .ranking-table_v2__project {    width: var(--cell-width);}}}

Примечание: если вы сделаете просто <style>без scoped и решите, что будет хорошей идеей редактировать глобальные стили вне окружения компонента, то у меня для вас плохие новости: лучше так не делать вне каких-то App.vue, и стоит ознакомиться с тем, что это за v-deep.

Поехали: добавляем Virtual Scroller, пихаем в него проекты, после чего выводим последние. Сразу скажу: поддержки Expandable Items у нас тут нет, я вынес информацию о проекте во всплывающее окно. Жаль, конечно, что нельзя это отображать прямо в таблице, как делал Vuetify, но тогда придется помучаться с их скроллером, а он и так не особо хорошо работает. В общем, к делу:

<v-virtual-scroll   class="ranking-table_v2__scroll"   :height="getSettings().commonHeight":item-height="getSettings().itemHeight":items="projects"><template #default="{item}"><div class="ranking-table_v2__project" :key="item.id"><!-- ... -->

Итого: на страницу, допустим, помещается 6 проектов (высота же у всех максимальная по факту), итого рендерится 6 строк + шапка. Колонок 50. Итого рендерится около 300 сложных компонентов. А вот это уже задача не уровня мстителей, 300 мы рендерить умеем.

Вспоминаем про лучший инструмент всех времен и народов v-lazy: он позволяет отрендерить элемент один раз и потом не перерендеривать его. Раньше мы пытались отрендерить 14 тысяч компонентов, сейчас 600 простых. Ну и оборачиваем все наши колонки (кроме шапки) в v-lazy. При горизонтальном скролле элементы подгружаются, и остаются отрендеренными до тех пор, пока сама строка таблицы не пропадет из области видимости и не разрендерится.

 <v-lazy class="ranking-table_v2__item ranking-table_v2__item--speciality"v-for="(speciality, index) in specialities":key="speciality.id">   <!-- Колонка в строке таблицы --></v-lazy>

Видео с разницей, можно сравнить:

Плюсы такого решения:

  • Элементы перестали скакать как ненормальные

  • Нет нужды писать свою реализацию логики рендера/отрендера

  • Можем отказаться от v-once и всяких принудительных ререндеров аля $forceUpdate

  • Куда больше гибкости при верстке таблицы

Минусы:

  • Если используются выпадающие элементы (expand), нужно писать свою реализацию

  • Нужно верстать таблицу самому, без инструментов движка

  • Нет средств сортировки/группировки и прочего (мне было не нужно)

  • Для того, чтобы шапка всегда была отрендеренной, но скроллилась вместе со всеми, мне пришлось отслеживать событие Scroll и подгонять скролл шапки к скроллу основных элементов

  • Чтобы отображать таблицу в нужном мне проценте от высоты страницы, мне пришлось смотреть на window.innerHeight и применять его в CSS переменных и в значении высоты у VirtualScroll

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

Заключение

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

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

И да: я пользовался дебагером производительности от Vue и смотрел, кто её потребляет. Зачастую там был буквально один-два компонента, и, заменив их на какой-то другой с похожей логикой, проблема не решалась - дело в их количестве, а не сложности (не считая таблицу Vuetify - там передается множество props'ов из компонента в компонент).

Надеюсь, что приведенные мной варианты натолкнут кого-то на решение его проблемы, а кто-то просто узнает что-то новое =). Будем вместе ждать стабильный Vue 3 со всей его экосистемой, как минимум Nuxt 3. Что-то обещают множество улучшений, может, часть костылей из этой статьи даже пропадет.

Подробнее..

Перевод Создание блога с помощью Nuxt Content(часть первая)

11.10.2020 18:05:36 | Автор: admin

Создание блога на Nuxt Content


От переводчика: Я собирался сделать собственную статью по Nuxt Content, но наткнулся на готовую статью, которая отлично раскрывает тему. Лучше у меня вряд ли получится, поэтому я решил перевести. Написал автору в твиттер и практически сразу получил согласие. Статья будет с моими дополнениями для лучшего понимания темы.



Модуль Content в Nuxt это headless CMS основанной на git файловой системе, которая предоставляет мощные функции для создания блогов, документации или просто добавления контента на обычный сайт. В этой статье мы разберем большинство преимуществ этого модуля и узнаем как создать блог с его помощью.


Видео обзор готового проекта:


Your browser does not support HTML5 video.

Посмотреть Демо /
Код проекта



Начало работы


Установка


Чтобы начать работу с модулем Content, нам сначала нужно установить модуль с помощью npm или yarn.


yarn add @nuxt/content

npm install @nuxt/content

Затем мы добавим его в сборку модулей в файле nuxt.config.


export default {  modules: ['@nuxt/content']}

Если вы создаете новый проект с помощью create-nuxt-app, можете выбрать опцию добавить модуль Content, и он будет установлен.

Создаем страницу


Модуль Content читает файлы в нашем каталоге content/.


mkdir content

Если вы создали свой проект с помощью create-nuxt-app, каталог content/ будет уже создан.

Давайте создадим директорию articles/, куда мы сможем добавлять статьи для нашего блога.


mkdir content/articles

Модуль Content может анализировать markdown, csv, yaml, json, json5 или xml файлы. Давайте создадим нашу первую статью в markdown файле:


touch content/articles/my-first-blog-post.md

Теперь добавим заголовок и текст для нашего сообщения в блоге:


# My first blog postWelcome to my first blog post using content module

В markdown мы создаем заголовок <h1> с помощью значка #. Убедитесь, что вы оставили пробел между ним и заголовком вашего блога. Для получения дополнительной информации о записи в markdown стиле смотрите Руководство по основному синтаксису.

Отображение контента


Чтобы отобразить контент на странице, мы используем динамическую страницу, добавив к странице знак подчеркивания (_). Создав компонент страницы с именем _slug.vue внутри папки blog, мы можем использовать переменную params.slug, предоставляемую vue router, для получения имени каждой статьи.


touch pages/blog/_slug.vue

Затем используем asyncData в компоненте страницы для получения содержимого статьи до того, как страница будет отрисована. Мы можем получить доступ к контенту через context, используя переменную $content. Поскольку мы хотим получить динамическую страницу, нам также необходимо знать, какую статью нужно получить с помощью params.slug, который доступен нам через context.


<script>  export default {    async asyncData({ $content, params }) {      // fetch our article here    }  }</script>

Внутри асинхронной функции asyncData мы создаем переменную с именем article, которая принимает контент, используя await, за которым следует $content. Нужно передать в $content параметры того, что мы хотим получить, в нашем случае это папка articles и slag, который мы получаем из params. По цепочке в конце добавляем метод fetch, который возвращает нужную статью.


<script>  export default {    async asyncData({ $content, params }) {      const article = await $content('articles', params.slug).fetch()      return { article }    }  }</script>

Чтобы отобразить контент, используем компонент <nuxt-content />, передав переменную в параметр document. В этом примере мы заключили его в HTML тег article, согласно правилам семантического синтаксиса, но вы можете использовать div или другой тег HTML, если хотите.


<template>  <article>    <nuxt-content :document="article" />  </article></template>

Теперь мы можем запустить сервер разработки и перейти по маршруту http://localhost:3000/blog/my-first-blog-post. Мы должны увидеть контент из .md файла.


статья из файла my-first-blog-post.md


Введенные переменные по умолчанию


Модуль Content Nuxt дает нам доступ к введенным переменным, которые мы можем показать в нашем шаблоне. Давайте посмотрим на переменные по умолчанию, которые вводятся в документ:


  • body: содержимое документа
  • dir: директория
  • extension: расширение файла (.md в этом примере)
  • path: путь к файлу
  • slug: имя файла
  • toc: массив, содержащий оглавление
  • createdAt: дата создания файла
  • updatedAt: дата последнего изменения файла

Мы можем получить доступ ко всем этим переменным, используя созданную ранее переменную article. Article это объект, который содержит все эти дополнительные введенные переменные, к которым у нас есть доступ. Давайте проверим их, распечатав с помощью тега <pre>.


<pre> {{ article }} </pre>

Теперь на нашей странице мы видим, что у нас есть объект с переменной, которая представляет собой пустой массив, и переменную содержимого(body), которая включает все наши теги h1 и p, а также некоторую другую информацию, которую мы рассмотрим позже. Если мы прокрутим вниз, вы увидите все остальные переменные, к которым есть доступ.


"dir": "/articles","path": "/articles/my-first-blog-post","extension": ".md","slug": "my-first-blog-post","createdAt": "2020-06-22T10:58:51.640Z","updatedAt": "2020-06-22T10:59:27.863Z"

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


<p>Post last updated: {{ article.updatedAt }}</p>

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


methods: {    formatDate(date) {      const options = { year: 'numeric', month: 'long', day: 'numeric' }      return new Date(date).toLocaleDateString('en', options)    } }

А затем в нашем шаблоне мы можем использовать метод formatDate, принимающий дату, которую мы получаем из контента, и возвращающий уже отформатированную дату.


<p>Article last updated: {{ formatDate(article.updatedAt) }}</p>

Пользовательские введенные переменные


Мы также можем добавить пользовательские введенные переменные, добавив блок YAML в наш md файл. Он должен находиться в верхней части файла, иметь допустимый формат YAML и находиться между двумя тройными пунктирными линиями. Это полезно для добавления переменных SEO, таких как заголовок, описание и изображение вашей статьи.


---title: My first Blog Postdescription: Learning how to use @nuxt/content to create a blogimg: first-blog-post.jpgalt: my first blog post---

Теперь у нас есть переменные title, description, img и alt, к которым у нас есть доступ из объекта article`.


<template>  <article>    <h1>{{ article.title }}</h1>    <p>{{ article.description }}</p>    <img      :src="article.image"      :alt="article.alt"    />    <p>Article last updated: {{ formatDate(article.updatedAt) }}</p>    <nuxt-content :document="article" />  </article></template>

Чтобы отрендерить изображения, включенные в YAML разделе файла, нам нужно либо поместить их в статическую папку, либо использовать синтаксис:
:src="require(`~/assets/images/${article.image}`)".
Изображения, включенные в содержимое статьи, всегда следует помещать в папку static, поскольку @nuxt/content не зависит от Webpack. Эта папка не пропускается через Webpack, в отличие от папки assets.

Стилизация markdown контента


Если мы посмотрим на код получившейся страницы, мы увидим, что все, что написано внутри нашего файла, заключено в div с классом nuxt-content. Это означает, что мы можем легко добавить стили ко всем нашим элементам из нашего markdown файла, заключив их в класс nuxt-content.


<style>  .nuxt-content h2 {    font-weight: bold;    font-size: 28px;  }  .nuxt-content h3 {    font-weight: bold;    font-size: 22px;  }  .nuxt-content p {    margin-bottom: 20px;  }</style>

Чтобы использовать стили с ограниченной областью видимости с классом nuxt-content, вам необходимо использовать deep селектор: /deep/, ::v-deep или >>>


Все остальные данные, которые поступают из YAML раздела, можно оформить как обычно: используя TailwindCSS или добавив в CSS в стиль тега.


Наши теги из md файла преобразуются в правильные теги, что означает, что теперь у нас есть два заголовка, два тега <h1>. Удалим один из md файла.


Добавление иконки к ссылке наших заголовков


Обратите внимание, что внутри тега <h2> есть тег <a> с href, который содержит id для ссылки на себя, и тег span внутри него с icon и icon-link классы. Это полезно для ссылки на этот раздел страницы. Ссылки в заголовках пусты и поэтому скрыты, поэтому давайте добавим им стиль. Используя классы значков, мы можем добавить svg-иконки в качестве фонового изображения для нашего значка. Сначала вам нужно будет добавить сами иконки в папку с ресурсами assets. В этом примере я добавила его в папку svg и взяла иконки Steve Schoger's Hero Icons.


.icon.icon-link {  background-image: url('~assets/svg/icon-hashtag.svg');  display: inline-block;  width: 20px;  height: 20px;  background-size: 20px 20px;}

Добавляем оглавление


Сгенерированная переменная toc позволяет нам добавить оглавление к нашему посту в блоге. Давайте добавим заголовки к нашему сообщению в блоге.


## This is a headingThis is some more info## This is another headingThis is some more info

Теперь мы можем видеть эти новые заголовки внутри массива toc с идентификатором, глубиной и текстом. Значение глубины является значением тега заголовка, поэтому значение глубины 2 приравнено тегу <h2> и равно 2, значение 3 тегу<h3> и т. д.


## This is a headingThis is some more info### This is a sub headingThis is some more info### This is another sub headingThis is some more info## This is another headingThis is some more info

Поскольку у нас есть доступ к toc и тексту, мы можем перебрать и отобразить их все, а в компоненте <NuxtLink> сделать ссылку на якорь раздела, на который мы хотим создать ссылку.


<nav>  <ul>    <li v-for="link of article.toc" :key="link.id">      <NuxtLink :to="`#${link.id}`">{{ link.text }}</NuxtLink>    </li>  </ul></nav>

Теперь ссылки ToC работают, и нажатие на любую из них приведет нас к нужной части документа. Модуль Content автоматически добавляет идентификатор и ссылку к каждому заголовку. Если мы проверим один из заголовков из нашего .md файла в инструментах разработки браузера, мы увидим, что у нашего тега <h2> есть идентификатор. Это тот же идентификатор, который находится в toc, который по сути из него и берется для ссылки на правильный заголовок.


Мы можем улучшить верстку дальше, используя динамические классы для стилизации классов заголовков в зависимости от глубины заголовка, которую мы можем добавить в наш тег nuxt-link. Если ссылка имеет глубину 2, добавьте отступ по оси y, а если глубина равна 3, добавьте поле слева и отступ внизу. Здесь мы используем классы TailwindCSS, но, конечно же, можно использовать собственные имена и стили классов.


:class="{ 'py-2': link.depth === 2, 'ml-2 pb-2': link.depth === 3 }"

Использование HTML в .md файлах


Иногда нам может понадобиться добавить HTML в наши файлы c разметкой. Давайте добавим div с некоторыми классами, чтобы он имел синий цвет фона с белым текстом, небольшим отступом и нижним краем.


<div class="bg-blue-500 text-white p-4 mb-4">  This is HTML inside markdown that has a class of note</div>

Добавление Vue компонента


А также мы можем добавлять Vue компоненты в .md файлы. Это означает, что если мы множество раз используем такие компоненты, как информационное окно или окно предупреждения, мы можем создать его с нужными нам стилями и передать текст в слот.


Теперь мы можем добавлять компоненты в наше приложение, установив для свойства components значение true в нашем файле nuxt.config. (начиная с v2.13)


export default {  components: true}

Автоматический импорт компонентов не будет работать для <nuxt-content>, если мы не зарегистрируем их глобально, добавив глобальную папку внутри папки компонентов.


mkdir components/global

А теперь можно создать наш компонент InfoBox внутри этой папки.


<template>  <div class="bg-blue-500 text-white p-4 mb-4">    <p><slot name="info-box">default</slot></p>  </div></template>

Теперь в нашей разметке эти компоненты будут доступны без необходимости их импорта.


<info-box>  <template #info-box>    This is a vue component inside markdown using slots  </template></info-box>

Глобальные компоненты будут доступны для всего нашего приложения, поэтому будьте осторожны при добавлении компонентов в эту папку. Это работает иначе, чем добавление компонентов в папку components, которые добавляются (наверное, имеется в виду импортируются прим. пер.) только в том случае, если они используются (начиная с Nuxt v2.13 компоненты в папке components импортируются автоматически, достаточно написать в Nuxt конфиге: components: true прим. пер.).



От переводчика: На этом первая часть статьи подошла к концу. Дебби познакомила нас с мощным инструментом от создателей Nuxt'а, который они, кстати, сами используют на своем сайте для документации фреймворка. В этом, в общем-то, основное его применение. Если вы думали, что наконец-то нашли простую CMS, на которой можно быстро шлепать проекты и отдавать заказчику, то это не так. Из-за git-подобной системы наполнения контента, проект должен всегда быть под контролем разработчиков. Она идеальна для документации инструментов разработки, и любого другого контента не требующего частого обновления. Если же контент должен динамически обновляться из-за действий пользователей, то тут без базы данных не обойтись.


В следующей части мы узнаем как стилизовать код в статьях, сортировать статьи по различным параметрам, работать с API Content и многое другое.


Продолжение следует...

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru