Nuxt.js: минималистичный фреймворк для создания универсальных приложений Vue.js

Spread the love

Универсальный (или изоморфный) — это термин, который стал очень распространенным в сообществе JavaScript. Он используется для описания кода JavaScript, который может выполняться как на клиенте, так и на сервере.

Многие современные JavaScript-фреймворки, такие как Vue.js, нацелены на создание одностраничных приложений (SPA). Это сделано для того, чтобы улучшить взаимодействие с пользователем и сделать приложения более быстрыми, поскольку пользователи хотят видеть обновления страниц мгновенно. Эти приложения называются приложения с рендерингом на стороне клиента. Хотя у этих приложений есть множество преимуществ, у них также есть два ключевых недостатка, первое длительное время первоначальной загрузки до момента отображение контента, и второй когда браузер получает код JavaScript, некоторые роботы поисковых систем или роботы социальных сетей не могут увидеть весь загруженный контент, при сканирование веб-страниц.

С другой стороны рендеринг на стороне сервера подразумевает предварительную загрузку приложений JavaScript на веб-сервере и отправку визуализированного HTML-кода в качестве ответа на запрос браузера на страницу. Создание подобных приложений может быть немного утомительным, так как требуется много настроек, прежде чем вы начнете кодировать. Эту проблему Nuxt.js стремится решить для приложений Vue.js.

Что такое Nuxt.js

Проще говоря, Nuxt.js — это инфраструктура, которая помогает вам легко создавать серверные приложения Vue.js. Он абстрагирует большую часть сложной конфигурации, связанной с управлением такими вещами, как асинхронные данные, промежуточное программное обеспечение (middleware) и маршрутизация. Это похоже на Angular Universal для Angular и Next.js для React.

Согласно документации Nuxt.js, — Его основной сферой применения является предоставление пользовательского интерфейса при абстрагировании от распределения клиент/сервер.

Статическая генерация

Еще одной замечательной особенностью Nuxt.js является его способность генерировать статические веб-сайты с помощью команды generate. Это довольно круто, и предоставляет функции, подобные популярным инструментам генерации статики, таким как Jekyll.

Под капотом Nuxt.js

В дополнение к Vue.js 2.0, Nuxt.js включает в себя следующее: Vue-Router, Vuex (включается только при использовании опции хранилища store option), Vue Server Renderer и vue-meta. Это замечательно, так как избавляет от необходимости вручную включать и настраивать различные библиотеки, необходимые для разработки приложения Vue.js. Nuxt.js делает все это из коробки, сохраняя общий размер 57kB min + gzip (60KB с vuex).

Nuxt.js также использует webpack с vue-loader и babel-loader для сборки, разделения кода (code-split) и минимизации кода.

Как это работает

Вот что происходит, когда пользователь посещает приложение Nuxt.js или переходит на одну из его страниц через <nuxt-link>:

  1. Когда пользователь первоначально посещает приложение, если в хранилище Vuex определено действие (action) nuxtServerInit, Nuxt.js вызовет его и обновляет хранилище.
  2. Затем он выполняет все middleware созданные для посещаемой страницы. Nuxt сначала проверяет файл nuxt.config.js на наличие глобального middleware, затем проверяет соответствующий файлы layout (для запрашиваемой страницы) и, наконец, проверяет страницу и ее дочерние элементы на наличие их middleware.
  3. Если посещаемый маршрут является динамическим, и для него существует метод validate(), маршрут проверяется этим методом.
  4. Затем Nuxt.js вызывает методы asyncData() и fetch() для загрузки данных перед отображением страницы. Метод asyncData() используется для извлечения данных и их рендеринга, а метод fetch() используется для заполнения хранилища перед рендерингом страницы.
  5. На последнем шаге отображается страница (содержащая все необходимые данные).

Эти действия выполняются по этой схеме, взятой из документации Nuxt:

Создание бессерверного статического сайта с Nuxt.js

В этой статье мы создадим простой статически сгенерированный блог с Nuxt.js. Мы предполагаем, что наши сообщения будут извлекаться по API, которое будем имитировать статическим файлом JSON.

Мы предполагаем что у вам базовые знания Vue.js. Если нет вы можете ознакомиться с великолепным руководством по началу работы с Vue.js 2.0 Джека Франклина.

Наше финальное приложение будет выглядеть как то так:

Весь код этой статьи можно увидеть на GitHub, а демо можно посмотреть здесь.

Начальная настройка приложения

Самый простой способ начать работу с Nuxt.js — это использовать шаблон, созданный командой Nuxt. Мы можем быстро установить его в наш проект (ssr-blog), используя vue-cli:

vue init nuxt/starter ssr-blog

После того, как вы запустите эту команду, откроется приглашение и вам будут заданы несколько вопросов. Вы можете нажать Return, чтобы принять ответы по умолчанию, или ввести свои собственные значения.

Примечание. Если у вас не установлена vue-cli, для ее установки необходимо сначала запустить npm install -g @vue/cli.

Далее мы устанавливаем зависимости проекта:

cd ssr-blog
npm install

Теперь мы можем запустить приложение:

npm run dev

Если все пойдет хорошо, вы сможете перейти на http://localhost:3000 и увидеть начальную страницу шаблона Nuxt.js. Вы даже можете просмотреть исходники страницы, чтобы увидеть, что весь контент, сгенерированный на странице.

Далее нам нужно сделать несколько простых настроек в файле nuxt.config.js:

// ./nuxt.config.js

module.exports = {
  /*
   * Headers of the page
   */
  head: {
    titleTemplate: '%s | Awesome JS SSR Blog',
    // ...
    link: [
      // ...
      {
        rel: 'stylesheet',
        href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css'
      }
    ]
  },
  // ...
}

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

Мы также подключили мой текущий CSS-фреймворк, Bulma, чтобы воспользоваться некоторыми предустановленными стилями. Это было сделано через опцию link.

Примечание. Nuxt.js использует vue-meta для обновления заголовков и атрибутов HTML приложений.

Теперь мы можем сделать следующие несколько шагов, добавив страницы и функции нашего блога.

Работа с макетами страниц (Page Layouts)

Во-первых, мы определим пользовательский базовый макет для всех наших страниц. Мы можем расширить основной макет Nuxt.js, обновив файл layouts/default.vue:

<!-- ./layouts/default.vue -->

<template>
  <div>
    <!-- navigation -->
    <nav class="navbar has-shadow" role="navigation" aria-label="main navigation">
      <div class="container">
        <div class="navbar-start">
          <nuxt-link to="/" class="navbar-item">
            Awesome JS SSR Blog!
          </nuxt-link>
          <nuxt-link active-class="is-active" to="/" class="navbar-item is-tab" exact>Home</nuxt-link>
          <nuxt-link active-class="is-active" to="/about" class="navbar-item is-tab" exact>About</nuxt-link>
        </div>
      </div>
    </nav>
    <!-- /navigation -->

    <!-- displays the page component -->
    <nuxt/>

  </div>
</template>

<style>
  .main-content {
    margin: 30px 0;
  }
</style>

В нашем пользовательском базовом макете мы добавли заголовок навигации сайта. Мы используем компонент <nuxt-link> для создания ссылок на маршруты, которые мы хотим иметь в нашем блоге. Вы можете прочитать документацию, если нужно узнать больше, о том как работает <nuxt-link>.

Компонент <nuxt> действительно очень важен при создании макета, так как именно он отображает компонент страницы.

Также здесь возможно сделать еще несколько вещей — например, определить пользовательские шаблоны документов и схемы ошибок — но мы не будем это делать так как они не нужны для нашего простого блога. Я призываю вас ознакомиться с документацией Nuxt.js по представлениям, чтобы узнать обо всех возможностях.

Простые страницы и маршруты

Страницы в Nuxt.js создаются как отдельные файловые компоненты в каталоге страниц pages. Nuxt.js автоматически преобразует каждый файл .vue в этом каталоге в маршрут приложения.

Создание домашней страницы блога

Далее добавим домашнюю страницу нашего блога, обновив файл index.vue, сгенерированный шаблоном Nuxt.js в каталоге pages:

<!-- ./pages/index.vue -->
<template>
  <div>
    <section class="hero is-medium is-primary is-bold">
      <div class="hero-body">
        <div class="container">
          <h1 class="title">
            Welcome to the JavaScript SSR Blog.
          </h1>
          <h2 class="subtitle">
            Hope you find something you like.
          </h2>
        </div>
      </div>
    </section>
  </div>
</template>

<script>
  export default {
    head: {
      title: 'Home'
    }
  }
</script>

<!-- Remove the CSS styles -->

Как указывалось ранее, указание опции title здесь автоматически вводит ее значение в значение titleTemplate перед рендерингом страницы.

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

Создание страницы About

Еще одна замечательная особенность Nuxt.js в том, что он будет мониторит изменения файлов внутри каталога pages, поэтому нет необходимости перезапускать приложение при добавлении новых страниц.

Мы можем проверить это, добавив простую страницу about.vue:

<!-- ./pages/about.vue -->
<template>
  <div class="main-content">
    <div class="container">
      <h2 class="title is-2">About this website.</h2>
      <p>Curabitur accumsan turpis pharetra <strong>augue tincidunt</strong> blandit. Quisque condimentum maximus mi, sit amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. Suspendisse potenti. Etiam mattis sem rhoncus lacus dapibus facilisis. Donec at dignissim dui. Ut et neque nisl.</p>
      <br>
      <h4 class="title is-4">What we hope to achieve:</h4>
      <ul>
        <li>In fermentum leo eu lectus mollis, quis dictum mi aliquet.</li>
        <li>Morbi eu nulla lobortis, lobortis est in, fringilla felis.</li>
        <li>Aliquam nec felis in sapien venenatis viverra fermentum nec lectus.</li>
        <li>Ut non enim metus.</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  head: {
    title: 'About'
  }
}
</script>

Теперь мы можем зайти по адресу http://localhost:3000/about, и увидеть страницу about, без необходимости перезапускать приложение.

Отображение сообщений блога на главной странице

Наша текущая домашняя страница довольно проста, так что нам нужно сделать ее лучше, отобразив на ней последние сообщения из блога. Мы сделаем это, создав компонент <posts> и разместив его на странице index.vue.

Но сначала мы должны получить сохраненные записи в блоге в виде JSON и поместить этот файл в корневой папке приложения. Файл можно скачать отсюда, или вы можете просто скопировать JSON ниже и сохранить в корневой папке как posts.json:

[
    {
        "id": 4,
        "title": "Building universal JS apps with Nuxt.js",
        "summary": "Get introduced to Nuxt.js, and build great SSR Apps with Vue.js.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "08:00 - 07/06/2017"
    },
    {
        "id": 3,
        "title": "Great SSR Use cases",
        "summary": "See simple and rich server-rendered JavaScript apps.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "17:00 - 06/06/2017"
    },
    {
        "id": 2,
        "title": "SSR in Vue.js",
        "summary": "Learn about SSR in Vue.js, and where Nuxt.js can make it all faster.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "13:00 - 06/06/2017"
    },
    {
        "id": 1,
        "title": "Introduction to SSR",
        "summary": "Learn about SSR in JavaScript and how it can be super cool.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "John Doe",
        "published": "11:00 - 06/06/2017"
    }
]

Примечание: в идеале сообщения должны быть получены по API из внешнего ресурса. Для этого можно использовать какой нибудь внешний сервис, например, Contentful

Компоненты размещаются в каталоге components. Там мы создадим отдельный файловый компонент <posts>:

<!-- ./components/Posts.vue -->
<template>
  <section class="main-content">
    <div class="container">
      <h1 class="title has-text-centered">
        Recent Posts.
      </h1>
      <div class="columns is-multiline">
        <div class="column is-half" v-for="post in posts" :key="post.id">
          <div class="card">
           <header class="card-header">
            <p class="card-header-title">
              {{ post.title }}
            </p>
          </header>
          <div class="card-content">
            <div class="content">
              {{ post.summary }}
              <br>
              <small>
                by <strong>{{ post.author}}</strong>
                \\ {{ post.published }}
              </small>
            </div>
          </div>
          <footer class="card-footer">
            <nuxt-link :to="`/post/${post.id}`"
              class="card-footer-item">
              Read More
            </nuxt-link>
          </footer>
        </div>
      </div>
    </div>
  </div>
</section>
</template>

<script>
  import posts from '~/posts.json'

  export default {
    name: 'posts',
    data () {
      return { posts }
    }
  }
</script>

Мы импортируем данные записей из сохраненного файла JSON и присваиваем их значению posts в нашем компоненте. Затем мы перебираем все записи в шаблоне компонента с помощью директивы v-for и отображаем нужные нам атрибуты записи.

Примечание. Символ ~ является псевдонимом для корневого каталога /. Вы можете просмотреть документацию здесь, чтобы узнать о различных псевдонимах, которые предоставляет Nuxt.js, и с какими каталогами они связаны.

Затем мы добавляем компонент <posts> на домашнюю страницу:

<!-- ./pages/index.vue -->
<template>
<div>
    <!-- ... -->
    <posts />
</div>
</template>

<script>
import Posts from '~/components/Posts.vue'

export default {
  components: {
    Posts
  },
  // ...
}
</script>

Добавление динамических маршрутов

Теперь мы добавим динамические маршруты для сообщений, чтобы мы могли получить доступ к выбранному сообщению, например, с помощью этого URL: /post/1.

Для этого мы добавим каталог post в каталог pages и структурируем его следующим образом:

pages
└── post
    └── _id
        └── index.vue

Такое расположение каталогов автоматически сгенерирует соответствующие динамические маршруты для приложения:

router: {
  routes: [
    // ...
    {
      name: 'post-id',
      path: '/post/:id',
      component: 'pages/post/_id/index.vue'
    }
  ]
}

Обновим файл выбранного сообщения ./pages/post/_id/index.vue:

<!-- ./pages/post/_id/index.vue -->
<template>
  <div class="main-content">
    <div class="container">
      <h2 class="title is-2">{{ post.title }}</h2>
      <div v-html="post.content"></div>
      <br>
      <h4 class="title is-5 is-marginless">by <strong>{{ post.author }}</strong> at <strong>{{ post.published }}</strong></h4>
    </div>
  </div>
</template>

<script>
  // import posts saved JSON data
  import posts from '~/posts.json'

  export default {
    validate ({ params }) {
      return /^\d+$/.test(params.id)
    },
    asyncData ({ params }, callback) {
      let post = posts.find(post => post.id === parseInt(params.id))
      if (post) {
        callback(null, { post })
      } else {
        callback({ statusCode: 404, message: 'Post not found' })
      }
    },
    head () {
      return {
        title: this.post.title,
        meta: [
          {
            hid: 'description',
            name: 'description',
            content: this.post.summary
          }
        ]
      }
    }
  }
</script>

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

  • Проверяем параметр маршрута с помощью метода validate. Он проверяет, является ли переданный параметр маршрута числом. Если он возвращает false, Nuxt.js автоматически загрузит страницу ошибки 404. Вы можете прочитать больше об этом здесь.
  • Метод asyncData используется для извлечения данных и их рендеринга на стороне сервера перед отправкой ответа в браузер. Он может возвращать данные разными способами. В нашем случае мы используем функцию обратного вызова для возврата сообщения с тем же атрибутом id, что и у параметра идентификатора маршрута id. Вы можете увидеть различные способы использования этой функции здесь.
  • Как мы уже видели, мы используем метод head для установки заголовков страницы. В этом случае мы меняем заголовок страницы на заголовок поста и добавляем summary поста в качестве мета-описания страницы.

Отлично, теперь мы можем снова посетить наш блог, чтобы увидеть, что все маршруты и страницы работают должным образом, а также просмотреть исходный код страницы, чтобы увидеть генерируемый HTML. У нас есть функциональное серверное JavaScript-приложение.

Генерация статических файлов

Далее мы можем сгенерировать статические HTML-файлы для наших страниц.

Нам нужно сделать небольшую настройку, поскольку по умолчанию Nuxt.js игнорирует динамические маршруты. Чтобы сгенерировать статические файлы для динамических маршрутов, нам нужно явно указать их в файле ./nuxt.config.js.

Мы будем использовать функцию обратного вызова, чтобы получить список наших динамических маршрутов:

// ./nuxt.config.js

module.exports = {
  // ...
  generate: {
    routes(callback) {
      const posts = require('./posts.json')
      let routes = posts.map(post => `/post/${post.id}`)
      callback(null, routes)
    }
  }
}

Чтобы сгенерировать все маршруты, мы можем запустить эту команду:

npm run generate

Nuxt сохраняет все созданные статические файлы в папке dist.

Вы можете почитать документацию по использованию всех свойств команды generate.

Развертывание на хостинге Firebase

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

Во-первых, установите Firebase CLI, если у вас его еще нет:

npm install -g firebase-tools

Чтобы подключить локальный компьютер к учетной записи Firebase и получить доступ к проектам Firebase, выполните следующую команду:

firebase login

Эта команда должно открыть окно браузера и предложит вам войти. После того, как вы вошли в систему, перейдите на страницу https://console.firebase.google.com и нажмите Добавить проект (Add project). Сделайте соответствующий выбор в открывшемся мастере.

После создания проекта перейдите на страницу хостинга проекта по адресу https://console.firebase.google.com/project/<project name>/hosting и завершите работу с мастером начала работы.

Затем на своем ПК из корневого каталога вашего проекта выполните следующую команду:

firebase init

В появившемся мастере выберите «Хостинг» (Hosting). Затем выберите ваш недавно созданный проект из списка вариантов. Затем выберите каталог dist в качестве публичного каталога. Выберите, настроить страницу как одностраничное приложение и, наконец, выберите «Нет», когда вас спросят, хотите ли вы перезаписать dist/index.html.

Firebase запишет пару файлов конфигурации в ваш проект, а затем запустит веб-сайт по адресу https://<project name>.firebaseapp.com. Демо-приложение для этой статьи можно увидеть на nuxt-ssr-blog.firebaseapp.com.

Если у вас возникнут проблемы, вы можете найти подробные инструкции на странице быстрого запуска Firebase.

Заключение

В этой статье мы узнали, как можно использовать преимущества Nuxt.js для создания серверных приложений JavaScript с Vue.js. Мы также узнали, как использовать команду generate для генерации статических файлов для наших страниц и быстрого их развертывания с помощью такой службы, как Firebase Hosting.

Фреймворк Nuxt.js действительно хорош. Эго даже рекомендуют в официальном GitBook Vue.js SSR. Я действительно с нетерпением жду возможности использовать его в других проектах SSR и изучить все его возможности.

Автор и оригинал статья Olayinka Omole Nuxt.js: a Minimalist Framework for Creating Universal Vue.js Apps

Была ли вам полезна эта статья?
[7 / 5]

Spread the love
Подписаться
Уведомление о
guest
2 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
It-boss
It-boss
3 лет назад

NuxtJS действительно хороший выбор. По крайней мере лучше чем Next+React так как nuxt+vue проще в освоении, и сообщество гораздо дружелюбнее.
…сложно было выбор сделать, но не жалею.

Геннадий
Геннадий
2 лет назад

Хорошая статья. Кратко и отмечены ключевые моменты.