Пару месяцев назад я написал эту статью о трех первых шагах Nuxt. Это была даже не RC-версия (релиз-кандидат), но ее принятие было очень впечатляющим. В связи с ажиотажем и общим интересом к этой новой технологии было возложено много ожиданий. Что еще более увлекательно, статья, которую я написал, набрала более 25 тысяч просмотров (и продолжает расти), поэтому было чертовски ясно, что в воздухе витает что-то горячее, и все сообщество не может дождаться стабильного релиза.

Итак, некоторое время назад, во время конференции Nuxt Nation, команда Nuxt анонсировала (наконец-то) новую и стабильную версию 3.0. И поскольку многие из нас не могут представить приложения Vue без Nuxt, поскольку он предоставляет множество дополнительных функций, которые могут помочь в ежедневной разработке, мы все были в восторге от того, что этот день наконец настал.

В этой статье я хотел бы погрузиться немного глубже, чтобы рассмотреть новый выпуск Nuxt и показать вам некоторые более продвинутые методы и варианты использования. В то же время я хочу призвать вас время от времени возвращаться к этой статье, потому что она будет прогрессивной. Что это значит? На мой взгляд, в новом Nuxt так много замечательных вещей, что мы не сможем пройти их все в одном письме. Так что сохраните это и установите несколько напоминаний, контент будет расти. Теперь начнем.

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

Список для чтения:

Авто Импорт

ХОРОШО. Начну с (на первый взгляд) очевидного. Наверное, все мы знаем, что мы можем импортировать components/ и composables/ автоматически. Это означает, что каждый файл, используемый в нашем шаблоне, не нуждается в импорте, потому что Nuxt делает это за нас. Однако, начиная со стабильной версии, теперь мы можем делать то же самое и с utils/ файлами. Таким образом, любая вспомогательная или служебная функция, экспортированная из этого каталога, также будет автоматически импортирована. Большое преимущество, особенно для любой компонуемой архитектуры.

Модули

modules и buildModules больше нет. Теперь все они посвящены времени сборки приложения Nuxt. Модули также асинхронны, и единственным способом контролировать их поведение в течение жизненного цикла приложения являются хуки. Они (модули) также получили новый и блестящий API, управляемый специальным nuxi/kit—, который должен помочь вам легко разрабатывать и создавать новые. Кроме того, стоит отметить, что в архитектуре нового модуля вы можете использовать плагины Vite, которые начинают быть очень мощными с самого начала разработки модуля. Более того, новый API kit предоставляет сложную цепочку инструментов, которая поможет вам еще больше внедрить в ядро ​​Nuxt.

Ознакомьтесь с (уже очень богатой) экосистемой модулей Nuxt 3.

Встроенные компонуемые

Каждый, кто пробовал Nuxt 2 раньше, знает нативные компоненты, которые поставляются с фреймворком: nuxt, nuxt-link или keep-alive. Они были здесь, чтобы помочь нам с рендерингом контента приложения, навигацией или управлением состоянием. Итак, с новой итерацией у нас снова есть все они, более того, теперь, благодаря компонуемому подходу Vue.js и Composition API внутри, команда Nuxt также предоставила некоторые встроенные композиции. О некоторых из них я хотел бы немного рассказать.

useCookie
Не знаю почему, но этот мне нравится больше всего. Может быть, из-за того, что мы создаем наши приложения, мы всегда склонны бороться с этой темой — куки. Работа на стороне клиента, на стороне сервера, их ограничения или простое API, которое отличается для разных библиотек. И именно поэтому я очень доволен тем, что они у нас есть с родными API Nuxt. Конструкция проста, вы просто можете их сохранить или прочитать, и в итоге вы получите ref объект с вашими данными. Наконец, у вас есть некоторые дополнительные параметры, такие как: maxAge, httpOnly, encode/decode или domain параметры решения по умолчанию, хорошо известные, которые могут помочь нам управлять файлами cookie еще проще.

Итак, у меня есть этот небольшой составной файл под названием useSession, который может помочь вам управлять и даже больше использовать помощника по файлам cookie от Nuxt. Проверьте это, возможно, вы найдете это полезным.

/* types */
type ObjectRecord<O> = { [K in keyof O]: O[K] };
type Session = {
  token: string;
};
type SessionStates = Partial<Session>
type CookieName = '@app[session]';
type CookieObject = ObjectRecord<SessionStates>;
type SetCookie<N = CookieName> = ($name: N, $data: SessionStates | null) => void;
type GetCookie = ($name: CookieName) => Ref<CookieObject> | Ref<null>;
type CookieProvider = {
  session: '@app[session]';
};

/* provider */
export const cookieProvider: ObjectRecord<CookieProvider> = {
  session: '@app[session]'
};

/* composable */
const useSession = () => {
  const { session } = cookieProvider
  
  const cookies = {
    [session]: getCookie(session) | ref<NullableRecord<SessionStates>>(null),
  };

  const setCookie: SetCookie<typeof session> = ($name, $data) => {
    cookies[$name] = useCookie($name);
    cookies[$name].value = { ...cookies[$name].value, ...$data };
  };
  const getCookie: GetCookie = ($name = session) => {
    return useCookie($name) || ref(null);
  };

  return {
    cookies,
    setCookie,
    getCookie,
  };
};

/* usage */
const { cookies, setCookie } = useSession()

Конечно, вы можете расширить его и добавить еще несколько имен файлов cookie для обработки некоторых внутренних сеансовых (или более) частей вашего приложения.

useFetch
О боже, это что-то. Прежде всего, вы должны знать, что useFetch компонуемый под капотом использует другой, который называется useAsyncData. Итак, этот отвечает за асинхронную выборку данных, как на стороне сервера, так и на стороне клиента.

function useAsyncData(
  handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
  options?: AsyncDataOptions<DataT>
): AsyncData<DataT>

Как видите, вы можете предоставить свой собственный сборщик/обработчик для получения данных из API. Здесь вы можете использовать нативную выборку, Axios или все, что вам нужно, что вам больше подходит. Следовательно, для команды Nuxt лучшим решением было создать собственный сборщик. И они сделали это, он называется ofetch, и вы также можете свободно его использовать, потому что он является частью цепочки инструментов с открытым исходным кодом Nuxts под названием unJS. Кроме того, вы можете передать здесь некоторые параметры, например, если вы хотите решить, хотите ли вы получать данные с сервера или со стороны клиента, или вы хотите преобразовать свои данные сразу после того, как они будут получены с сервера, но не переданы в шаблон. еще и многое другое (просто посмотрите здесь). useFetch тогда (можно сказать, что) является своего рода оберткой над useAsyncData и этим новым решением $fetch. Он обойдет опции useAsyncData и предоставит несколько больше, предназначенных для вызовов API, типичных для таких запросов к серверу. В него даже встроены перехватчики. Необычно!

function useFetch(
  url: string | Request | Ref<string | Request> | () => string | Request,
  options?: UseFetchOptions<DataT>
): Promise<AsyncData<DataT>>

В то же время это своего рода контракт — вы не можете контролировать инструмент и механизмы вашего HTTP-клиента, но последовательно это делается для вас, чтобы помочь вам создавать приложения быстрее и эффективнее. В конце концов, если вам нужно какое-то более сложное решение, чтобы иметь больше контроля над ним, вы можете использовать свой собственный сборщик/обработчик в составном файле useAsyncData. К сожалению, вы не можете обернуть useFetch своей собственной настройкой. Но давайте, это встроенная функция, о которой вам не нужно заботиться и думать. Он у вас здесь, и он ждет, чтобы получить некоторые данные для вас, и это супер круто!

useState
Думая об этом составном, мне хочется смеяться. Почему ты спрашиваешь? Итак, у нас уже были Vuex в экосистеме и новейшая блестящая и мощная Pinia. И еще больше таких инструментов, как Использовать эффект состояния. Но, может быть, давайте добавим в фреймворк внутреннее управление состоянием? И они сделали это. Возможно, это не так сложно, как инструменты, упомянутые выше, но работает очень хорошо. Более того, он интуитивно понятен, построен с использованием того же API, что и useCookie, и, наконец, он интегрирован в Nuxt, поэтому вам не нужны никакие другие дополнительные зависимости.

useState<T>(key: string, init?: () => T | Ref<T>): Ref<T>

Если вы создаете компонуемое фронтальное приложение, с многократно используемой логикой вам больше ничего не нужно. Одна вещь может быть лучше. Толлинг вокруг него. Если вы уже пробовали Pinia, вы знаете, что она поставляется со всеми блестящими игрушками, такими как инструменты разработки с жизненными циклами состояний, мониторингом и отслеживанием данных о состоянии. Но. прожить без него точно можно — опять же, если вам не нужны никакие навороченные.

useRuntimeConfig
Возможно, у вас была возможность использовать runtimeConfig и privateRuntimeConfig в Nuxt 2. Оба предоставляли конфигурацию в приложении, используемую публично и конфиденциально — только на сервере. Теперь, с Nuxt 3, у нас есть только один runtimeConfig с новым API, настройкой и возможностью компоновки в качестве обработчика. Давайте немного углубимся в это. Таким образом, внутренняя конфигурация приложения должна быть определена внутри файла nuxt.config.ts. Так.

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '123',
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
    }
  }
})

Таким образом, каждое значение, определенное в основном объекте runtimeConfig, по умолчанию будет приватным, а каждое добавленное в public, конечно же, будет общедоступным и доступным на стороне клиента. Отлично, как теперь получить к ним доступ? Это супер просто, вы уже назвали, новый useRuntimeConfig компонуемый. Вы можете использовать это так.

const config = useRuntimeConfig();

config.apiSecret; // only server-side
config.public.apiBase;

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

Динамические компоненты

Я не знаю, знакомы ли вы уже с этой функцией, но проверьте этот код, код из приложения Vue 2, встроенный поверх API параметров.

<template>
  <div class="componentWrapper">
    <component :is="componentLoader">
    </component>
  </div>
</template>

<script>
export default {
  name: 'component-wrapper',
  props: {
    componentFile: {
      type: String,
      default: () => null
    }
  },
  computed: {
    componentLoader () { 
      return () => import('./${this.componentFile}
    }
  }
}
</script>

Здесь вы можете увидеть использование свойства computed для предоставления/разрешения динамического значения (пути) в качестве свойства для динамического импорта компонента без его загрузки в процессе рендеринга.

Это была суперская вещь.

Итак, я пытался добиться того же результата, что и с Vue 2, но безрезультатно. Есть несколько способов попробовать это с resolveComponent и defineAsyncComponent, но с текущей реализацией механики свойств computed это уже невозможно. Теперь он создает ссылку на реактивный объект ref и не может быть динамически разрешен, затем загружен как компонент поверх значения свойства и, наконец, отрендерен Vite. Да, я знаю, это немного сложно. Теперь вам нужно каким-то образом указать предопределенный путь — внутри или снаружи компонента, условно или в виде списка JSON, независимо от того, как вам нужно это сделать. Итак, как это сделать? Построив что-то подобное.

<template>
  <component :is="componentLoaderInit" />
</template>

<script setup lang="ts">
interface DynamicComponent{
  title?: string;
  subtitle?: NullableRecord<string>;
  file: string | StringConstructor;
}
interface Props {
  component: DynamicComponent;
}

defineOptions({
  name: 'DynamicComponentProvider',
  inheritAttrs: false,
});

const props = defineProps<Props>();

const componentLoader: () => ComponentDefinition | null = () => {
  let component: ComponentDefinition | null = null;
  switch (props.component.file) {
    case 'componentName':
      component = defineAsyncComponent({
        loader: () => /* @vite-ignore */ import(`@/components/componentName.vue`),
      });
      break;
  }
  return component;
};
const componentLoaderInit = computed(() => componentLoader());
</script>

Здесь вы видите довольно быструю нотацию switch/case, которая обеспечивает выбор пути к компоненту поверх динамического свойства component.file. Кроме того, вы должны предоставить Vite правильный псевдоним, и если у вас возникнут проблемы с его распознаванием в IDE, используйте комментарий @vite-ignore. Вот и все. Теперь вы можете использовать его так.

<component-provider :component="{ file: 'componentName' }" />

Просто, быстро и при этом динамично? Может быть, у вас есть лучшее решение? Подскажите, хотелось бы знать наверняка!

Это все на данный момент. Как я уже упоминал в начале этой статьи, я буду добавлять что-то новое каждый день, просто заходите время от времени, и в то же время это будет огромная база знаний о более продвинутых функциях и решениях Nuxt. . Прямо сейчас вы можете ожидать новые разделы о плагинах Vite, макросах и интерфейсах Props. Некоторые из этих вещей могут быть не специфичны для Nuxt, больше для тем, связанных с Vue, но эй, в конце концов приложение Nuxt остается приложением Vue.

Удачи и наслаждайтесь. Может, подумайте о том, чтобы купить мне кофе.