Skip to content

3. Перший додаток [nuxt.js]

3.1. Створення додатка

Для наших розробок [nuxt.js] ми продовжуємо використовувати VS Code. Ми створили порожню папку [dvp], у яку будемо розміщувати наші приклади. Потім відкриваємо цю папку:

Image

Зберігаємо робочу область під назвою [intro-nuxtjs] [3-5]:

Image

Відкриваємо термінал [6-7]:

Image

До цього моменту ми використовували менеджер пакетів JavaScript [npm]. Для різноманітності ми використаємо тут менеджер [yarn]. Він, як і [npm], встановлюється разом з останніми версіями [node.js]. Щоб створити перший додаток [nuxt], ми використовуємо команду [yarn create nuxt-app <dossier>] [1]. Команда запросить певну інформацію про проект, який потрібно згенерувати, а після її отримання згенерує його [2]:

Image

У [2] було створено цілу структуру файлів. Файл [package.json] містить перелік бібліотек JavaScript, завантажених у папку [node-modules] [4]:


{
  "name": "nuxt-intro",
  "version": "1.0.0",
  "description": "nuxt-intro",
  "author": "serge-tahe",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
  },
  "dependencies": {
    "nuxt": "^2.0.0",
    "bootstrap-vue": "^2.0.0",
    "bootstrap": "^4.1.3",
    "@nuxtjs/axios": "^5.3.6"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
  }
}

Цей файл відображає відповіді, отримані на команду [create nuxt-app] для визначення створеного проєкту (листопад 2019 року). У читача може бути інший файл [package.json]:

  • він міг надати інші відповіді на запитання;
  • команда [create nuxt-app] зазнала змін з моменту написання цього документа: змінилися залежності та версії;

Рядок 8 скрипта — це команда, яка запускає додаток:

Image

  • у [4] видно, що додаток доступний за адресами URL та [localhost:3000];
  • у [5-6] видно, що додаток створює сервер [6] та клієнт (цього сервера) [5];

Відкриємо URL [http://localhost:3000/] у браузері:

Image

3.2. Опис структури дерева додатка [nuxt]

Повернемося до структури створеного додатка:

Image

Роль папок така:

assets
некомпільовані ресурси додатка (зображення тощо);
static
файли з цієї папки будуть доступні в кореневому каталозі додатка. У цю папку поміщають файли, які повинні знаходитися в кореневому каталозі додатка, наприклад, файл [robots.txt], призначений для пошукових систем;
components
компоненти [vue] додатка, що використовуються у файлах [layouts] та [pages];
layouts
компоненти [vue] додатка, що слугують макетами для [pages];
сторінки
компоненти [vue], що відображаються різними маршрутами додатка. Їх можна назвати поданнями додатка. Сторінки відіграють особливу роль у [nuxt]: маршрути створюються динамічно на основі дерева, що міститься у папці [pages];
проміжне програмне забезпечення
скрипти, що виконуються при кожній зміні маршруту. Вони дають змогу керувати цими маршрутами;
плагіни
має назву, що може ввести в оману. Може містити як плагіни, так і звичайні скрипти. Скрипти, що знаходяться в цій папці, виконуються під час запуску додатка;
store
якщо він містить скрипт [index.js], то цей скрипт визначає екземпляр сховища [Vuex];

Якщо папка порожня, її можна видалити з дерева каталогів. У наведеному вище прикладі папки [assets, static, middleware, plugins, store] та [2] можна видалити.

3.3. Файл конфігурації [nuxt.config]

Робота програми керується таким файлом [nuxt.config.js]:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: process.env.npm_package_description || ''
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Документація: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Документація: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Документація: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • рядок 2: тип створеного додатка:
    • [universal]: клієнтсько-серверний додаток. Під час початкового завантаження додатка, а також під час кожного оновлення сторінки в браузері, до сервера надходить запит на надання сторінки;
    • [sap]: додаток типу [Single Page Application]: сервер спочатку надає весь додаток. Далі клієнт працює самостійно, навіть у разі оновлення сторінки в браузері;
  • рядки 6–18: визначають заголовок HTML <head> для різних сторінок додатка:
    • рядок 7: тег <title> заголовка сторінок;
    • рядки 8–16: теги <meta>;
    • рядок 17: теги <link>

У згенерованому додатку тег <head> має такий вигляд (вихідний код сторінки, що відображається в браузері):


<title>nuxt-intro</title>
<meta data-n-head="ssr" charset="utf-8">
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
<meta data-n-head="ssr" data-hid="description" name="description" content="nuxt-intro">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="preload" href="/_nuxt/runtime.js" as="script">
<link rel="preload" href="/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

Тепер змінимо файл [nuxt.config] наступним чином:


head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},

Коли ми запускаємо додаток знову, тег <head> має такий вигляд (вихідний код сторінки, що відображається в браузері):


  <head >
    <title>Introduction à [nuxt.js]</title>
    <meta data-n-head="ssr" charset="utf-8">
    <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
    <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
    <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
    <link rel="preload" href="/_nuxt/runtime.js" as="script">
    <link rel="preload" href="/_nuxt/commons.app.js" as="script">
    <link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

Повернемося до файлу [nuxt.config]:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    ...
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Документація: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Документація: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Документація: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • рядок 12: між кожним маршрутом клієнта [nuxt] з’являється смуга завантаження (loading), якщо зміна маршруту займає трохи часу. Властивість [loading] дозволяє налаштувати цю смугу завантаження, у даному випадку — колір смуги;
  • рядок 16: глобальні файли [css]. Вони автоматично включатимуться на всі сторінки додатка;
  • рядки 24–27: модулі JavaScript, необхідні для компіляції (build) додатка;
  • рядки 31–36: модулі JavaScript, які використовує додаток;
  • рядок 41: налаштування бібліотеки [axios], якщо її було обрано користувачем для діалогових вікон HTTP із сторонніми серверами;
  • рядки 45–50: налаштування компіляції (build) проєкту;

До файлу конфігурації можна додати інші ключі. Зокрема, можна налаштувати порт служби (за замовчуванням — 3000) та кореневу папку проєкту (за замовчуванням — коренева папка проєкту). Саме це ми й зробимо зараз, додавши такі ключі:


// каталог з вихідним кодом
  srcDir: '.',
  router: {
    // URL кореневий каталог сторінок додатка
    base: '/nuxt-intro/'
  },
  // сервер
  server: {
    // порт служби — за замовчуванням 3000
    port: 81,
    // мережеві адреси, на які здійснюється прослуховування — за замовчуванням localhost=127.0.0.1
    host: '0.0.0.0'
  }
  • рядок 2: де знайти вихідний код проекту. Він знаходиться тут, у поточній папці, тобто на тому ж рівні, що й файл [nuxt.config.js]. Це значення за замовчуванням;
  • рядки 8–13: налаштовують сервер (не слід забувати, що додаток [nuxt] типу [universal] встановлено одночасно на сервері та в клієнтському браузері цього сервера);
  • рядок 10: сторінки додатка будуть подаватися на порту 81 сервера;
  • рядок 12: за замовчуванням [localhost] (мережева адреса 127.0.0.1). Комп’ютер може мати кілька мережевих адрес, якщо він належить до кількох мереж. Адреса 0.0.0.0 означає, що веб-сервер прослуховує всі мережеві адреси комп’ютера;
  • рядки 3–6: налаштовують маршрутизатор додатка [nuxt];
  • рядок 5: сторінки додатка будуть доступні за адресами URL та [http://localhost:81/nuxt-intro/];

Додамо ці рядки до файлу [nuxt.config.js], а потім запустимо проєкт (скрипт npm dev). Результат такий:

Image

  • у [1] — адреса комп’ютера в публічній мережі;
  • у [2] — порт служби;
  • [3] — кореневий каталог додатка;

3.4. Папка [layouts]

Image

Папка [layouts] призначена для компонентів верстки. За замовчуванням використовується компонент із назвою [default.vue]. У цьому проєкті це такий компонент:


<template>
  <div>
    <nuxt />
  </div>
</template>

<style>
html {
  font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 16px;
  word-spacing: 1px;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: border-box;
  margin: 0;
}

.button--green {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #3b8070;
  color: #3b8070;
  text-decoration: none;
  padding: 10px 30px;
}

.button--green:hover {
  color: #fff;
  background-color: #3b8070;
}

.button--grey {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #35495e;
  color: #35495e;
  text-decoration: none;
  padding: 10px 30px;
  margin-left: 15px;
}

.button--grey:hover {
  color: #fff;
  background-color: #35495e;
}
</style>

Коментарі

  • рядки 1–5: компонент [template];
  • рядок 3: тег <nuxt /> позначає поточну сторінку маршрутизації;
  • рядки 7–55: стиль, вбудований у компонент верстки. Оскільки він містить поточну сторінку маршрутизації, цей стиль застосовуватиметься до всіх маршрутизованих сторінок додатка;

Бачимо, що основною метою сторінки [default.vue] є застосування стилю до маршрутизованих сторінок.

3.5. Папка [pages]

Image

Папка [pages] містить маршрутизовані подання, які бачить користувач. Сторінка [index.vue] є головною сторінкою додатка. У папці [nuxt.js] немає файлу маршрутизації. Маршрути визначаються на основі структури папки [pages]. У цьому випадку наявність файлу [index.vue] автоматично створить маршрут із назвою [index] та шляхом [/index], скороченим до [/], оскільки це головна сторінка. Таким чином створюється наступний маршрут:

        { name : ‘index’, path : ‘/’}

Файл [index.vue] має такий вигляд:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>

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

export default {
  components: {
    Logo
  }
}
</script>

<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

У файлі [template] у рядках 1–25 відображається таке:

Image

Зображення [1] генерується рядком 4 файлу [template]. Отже, бачимо, що сторінка використовує компонент із назвою [logo]. Він визначений у рядках 27–35 скрипта сторінки. У рядку 28 позначення [~] вказує на корінь проєкту.

Image

Компонент [Logo.vue] має такий вигляд:


<template>
  <div class="VueToNuxtLogo">
    <div class="Triangle Triangle--two" />
    <div class="Triangle Triangle--one" />
    <div class="Triangle Triangle--three" />
    <div class="Triangle Triangle--four" />
  </div>
</template>

<style>
.VueToNuxtLogo {
  display: inline-block;
  animation: turn 2s linear forwards 1s;
  transform: rotateX(180deg);
  position: relative;
  overflow: hidden;
  height: 180px;
  width: 245px;
}

.Triangle {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
}

.Triangle--one {
  border-left: 105px solid transparent;
  border-right: 105px solid transparent;
  border-bottom: 180px solid #41b883;
}

.Triangle--two {
  top: 30px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 87.5px solid transparent;
  border-right: 87.5px solid transparent;
  border-bottom: 150px solid #3b8070;
}

.Triangle--three {
  top: 60px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 70px solid transparent;
  border-right: 70px solid transparent;
  border-bottom: 120px solid #35495e;
}

.Triangle--four {
  top: 120px;
  left: 70px;
  animation: godown 0.5s linear forwards 3s;
  border-left: 35px solid transparent;
  border-right: 35px solid transparent;
  border-bottom: 60px solid #fff;
}

@keyframes turn {
  100% {
    transform: rotateX(0deg);
  }
}

@keyframes godown {
  100% {
    top: 180px;
  }
}

@keyframes goright {
  100% {
    left: 70px;
  }
}
</style>

Цей компонент в основному складається зі стилів та анімацій для створення анімованого зображення.

3.7. Вигляд DevTools

[Vue DevTools] — це розширення для браузера, яке дозволяє перевіряти об’єкти [nuxt.js] та [vue.js] у браузері. Ми вже використовували його в розділі, присвяченому [vue.js]. Давайте розглянемо, що виявляє цей інструмент під час відображення головної сторінки нашого додатка:

Image

  • у [1] компонент [PagesIndex] позначає сторінку [pages/index.vue];
  • у [2] бачимо, що цей компонент має властивість [$route], яка є маршрутом, що привів до сторінки [index];

Як просте завдання, виведемо цей шлях у консоль.

3.8. Зміна головної сторінки

Ми змінимо файл [index.vue]. У нашій установці проєкту ми встановили дві залежності:

  • [eslint]: перевіряє синтаксис файлів JavaScript та компонентів Vue. Якщо було встановлено розширення [ESLint] до VSCode, цей синтаксис перевіряється під час введення тексту, а помилки негайно виявляються;
  • [prettier]: форматує код JavaScript за стандартними правилами;

Ці залежності записані у файлі [package.json]:


"devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
}

Я помітив (листопад 2019 року), що при встановленні за допомогою команди [yarn create nuxt-app] інструменти [eslint, prettier] не працюють під час введення тексту. Помилки виявляються лише під час компіляції. Після деяких пошуків я знайшов конфігурацію, яка працює:

Image

У кореневій папці проєкту слід створити папку [.vscode], у якій має бути такий файл [settings.json]:


{
  "eslint.validate": [
    {
      "language": "vue",
      "autoFix": true
    },
    {
      "language": "javascript",
      "autoFix": true
    }
  ],
  "eslint.autoFixOnSave": true,
  "editor.formatOnSave": false
}
  • рядки 2–11: вказують, що під час перевірки файлів .vue та .js програма [eslint] має виправляти ті помилки, які вона може виправити;
  • рядок 12: під час збереження файлу [eslint] повинен виправляти ті помилки, які він може виправити;
  • рядок 13: вимикає форматування, яке за замовчуванням виконується в VSCode під час збереження. Це робитиме [prettier];

Завдяки цій конфігурації:

  • помилки синтаксису або форматування виявляються одразу під час введення тексту;
  • помилки форматування автоматично виправляються під час збереження файлу;

Бібліотека [prettier] налаштовується за допомогою файлу [.prettierrc]:

Image

За замовчуванням це такий файл:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true
}
  • рядок 1: відсутність символу «;» в кінці команд;
  • рядок 2: якщо функція «стрілка» (arrow) має єдиний параметр, він береться в дужки;
  • рядок 3: символьні рядки беруться в апострофи (не в лапки);

Додаємо такі два правила:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true,
  "printWidth": 120,
  "endOfLine": "auto"
}
  • рядок 5: рядок коду може містити до 120 символів;
  • рядок 6: позначка кінця рядка може бути як CRLF (Windows), так і LF (Unix);

Нарешті, файл [package.json] змінюється наступним чином:


"scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lintfix": "eslint --fix --ext .js,.vue --ignore-path .gitignore ."
},
  • рядок 7: додаємо команду [lintfix], яка є ідентичною команді [lint] із рядка 6, за винятком того, що вона додатково містить параметр [--fix]. Команда [lint] перевіряє синтаксис і формат усіх файлів проєкту та повідомляє про будь-які помилки. [lintfix] виконає те саме, за винятком того, що проблеми з форматуванням, які можна виправити, будуть виправлені автоматично. [lintfix] — це команда, яку слід використовувати, якщо компіляція завершується з помилкою через проблеми з форматуванням файлів;

Зробивши це, ми змінюємо файл [index.vue] наступним чином:

Image


<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  // життєвий цикл
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>
  • рядки 10–12: додаємо функцію [created], яка автоматично виконується після створення компонента;
  • рядок 11: відображається поточний маршрут;
  • рядок 2: коментар, призначений для [eslint]. Без цього коментаря [eslint] повідомляє про помилку; рядок 11: не допускаються інструкції [console] у функціях життєвого циклу. [eslint] можна налаштувати. Ми збережемо його налаштування за замовчуванням і будемо використовувати коментарі, подібні до того, що наведено у рядку 2, щоб вимкнути конкретне правило [eslint]. Ми будемо використовувати два типи коментарів:
    • /* вимкнення правила [eslint] */: вимкнення правила для всього файлу;
    • // вимкнення правила [eslint]: вимкнення правила для наступного рядка;

Під час введення тексту помилки позначаються, а також доступна функція [Quick Fix]:

Image

Запускаємо проект:

Image

  • у [1], вкладка [Vue] у інструментах розробки браузера (F12);
  • у [2] та [3] — відображення маршруту;

Чому два види відображення, а не один?

Додаток [nuxt] складається з двох елементів: сервера та клієнта:

  1. сервер надає сторінки додатка під час його запуску, а потім щоразу, коли сторінка оновлюється в браузері (F5) або коли користувач вручну вводить URL додатка;
  2. кожна сторінка, що надається браузером, містить запитувану сторінку, а також код JavaScript усього додатка, який потім виконується в браузері. Це і є клієнт. Доки сторінка в браузері не оновлюється, додаток працює як класичний додаток Vue у режимі [sap] (Single Page Application). Як тільки користувач вручну ініціює оновлення сторінки, запит на неї надсилається до сервера, і процес повертається до попереднього етапу 1.

Слід розуміти, що сервер і клієнт надають одні й ті самі сторінки з папки [pages]. З цієї причини розробники [nuxt] називають цей тип сторінок ізоморфними. Одні й ті самі сторінки [.vue] можуть інтерпретуватися як клієнтом, так і сервером. Розглянемо приклад сторінки [index]:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>

<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  // життєвий цикл
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>

Оскільки це головна сторінка, під час запуску додатка її подає сервер. Сторінка на сервері також має життєвий цикл, такий самий, як і у класичної сторінки [Vue], за винятком функцій [beforeMount, monted], які не існують на стороні сервера. Функція [created] виконується, що пояснює перший запис у журналі. Це, до речі, означає, що сервер здатний виконувати скрипти JavaScript. У цьому випадку, як і загалом, цей сервер є сервером [node.js]. Після створення сторінки на сервері вона надходить до браузера, де знову проходить цикл життя. Функція [created] виконується вдруге, що дає другий запис у журналі.

Архітектура додатка [nuxt] може виглядати наступним чином:

Image

  • [1]: браузер, який розміщує додаток [nuxt] після його завантаження в браузер. Це те, що ми називаємо клієнтом [nuxt];
  • [3]: сервер, на якому спочатку розміщується додаток [nuxt]. Цей файл завантажується у браузер [1] під час запуску додатка та щоразу, коли користувач оновлює поточну сторінку браузера або вручну вводить URL додатка. Саме в цьому полягає відмінність у роботі порівняно з класичним додатком на Vue. У класичному додатку Vue, після завантаження в браузер, сервер більше ніколи не залучався. Ще одна важлива відмінність, яку ми поки що не змогли помітити, полягає в тому, що сервер додатка на базі Vue є статичним і не здатний інтерпретувати сторінки [.vue], тоді як сервер додатка Nuxt типу [universal] є сервером на базі JavaScript. Перед тим, як надіслати сторінку до браузера, сервер може виконати скрипти та, наприклад, отримати дані з сервера [2];
  • [2]: це сервер, який надає дані або клієнтам [nuxt] та [1], або серверам [nuxt] та [3];

На наведеному вище схематичному зображенні можна виділити три підсистеми «клієнт/сервер»:

  • [1, 3]: розміщує додаток [nuxt]. [3] надає її під час запуску додатка разом із початковою сторінкою та щоразу, коли користувач вручну запитує сторінку. [1] розміщуєдодаток [nuxt], отриманий від [3], який у свою чергу працює в режимі [SAP], доки сторінки не запитуються вручну у [3];
  • [1, 2]: у режимі [SAP] клієнт [nuxt] отримує зовнішні дані з одного або декількох серверів;
  • [3, 2]: під час формування сторінки, яку запитує користувач, сервер [3] також може отримувати зовнішні дані з одного або декількох серверів;

Отже, саме сервер [3] розрізняє додаток [nuxt] від додатка [vue]. Цей сервер задіюється щоразу, коли користувач вручну запитує сторінку. Він обробляє ті самі сторінки [.vue], що й клієнт [vue] та [1]. Це сервер JavaScript, здатний виконувати скрипти, що містяться на сторінці. Це може, наприклад, змінити спосіб формування головної сторінки з використанням зовнішніх даних: тоді як додаток [vue] обов’язково отримує їх від клієнта [1], тут їх можна отримати через сервер [3] ще до того, як сторінка буде надіслана клієнту. Таким чином, головна сторінка набуває більшого значення і може сприяти покращенню SEO додатка.

Примітка: у режимі розробки ці три об’єкти [1, 2, 3] часто знаходяться на одній машині. Так буде у всіх наших прикладах.

3.9. Переміщення вихідного коду додатка в окрему папку

Далі ми створимо різні додатки [nuxt] у тій самій папці [dvp]. Адже папка залежностей [node_modules], що генерується для кожного проєкту [nuxt], може займати кілька сотень мегабайтів. Ми створимо кілька папок [nuxt-00, nuxt-01, ...] у папці [dvp] для розміщення вихідного коду прикладів, що підлягають тестуванню. Потім ми використаємо файл конфігурації [nuxt-config.js], щоб вказати, де знаходиться вихідний код проекту [dvp], який залишиться єдиним проектом [nuxt] у цьому посібнику.

Ми перемістимо вихідний код додатка, спочатку згенерованого командою [yarn create nuxt-app], у папку [nuxt-00]:

Image

  • у [2] ми перемістили папки [components, layouts, pages] у папку [nuxt-00];
  • у [3] нам потрібно змінити файл [nuxt.config.js];

Ми змінюємо файл [nuxt.config.js] наступним чином:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  ...
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },
  // каталог вихідного коду
  srcDir: 'nuxt-00',
  // маршрутизатор
  router: {
    // кореневий каталог URL додатка
    base: '/nuxt-00/'
  },
  // сервер
  server: {
    // порт служби, за замовчуванням 3000
    port: 81,
    // мережеві адреси, на яких відбувається прослуховування, за замовчуванням localhost: 127.0.0.1
    // 0.0.0.0 = усі мережеві адреси комп’ютера
    host: '0.0.0.0'
  }
}

Файл змінено у двох місцях:

  • рядок 17: вказується, що вихідний код проєкту [dvp] знаходиться у папці [nuxt-00];
  • рядок 21: вказується, що кореневою папкою додатка URL тепер є [/nuxt-00/]. Ця зміна не була обов’язковою. Цю властивість можна було б не вказувати, і тоді кореневим каталогом URL став би [/]. У даному випадку це дозволить нам запам’ятати, що виконується вихідний код із папки [nuxt-00];

Після цього проект [dvp] виконується так само, як і раніше:

Image

3.10. Розгортання додатка [nuxt-00]

Ми запустимо додаток [nuxt-00] у середовищі, відмінному від вбудованого середовища VSCode.

Спочатку ми компілюємо додаток:

Image

  • у [3] — результат компіляції клієнта. Буде виконуватися браузером;
  • у [4] — результат компіляції сервера. Буде виконуватися сервером [node.js];

Результат компіляції розміщується у папці [.nuxt]:

Image

Копіюємо папки [.nuxt, node_modules] та файли [package.json, nuxt.config.js] в окрему папку:

Image

Файл [package.json] спрощується наступним чином:


{
  "scripts": {
    "start": "nuxt start"
  }
}
  • залишається лише скрипт [start], який дозволяє запустити скомпільовану версію проєкту;

Файл [nuxt.config.js] спрощується наступним чином:


export default {
  // маршрутизатор
  router: {
    // кореневий каталог URL додатка
    base: '/nuxt-00/'
  },
  // сервер
  server: {
    // порт служби, за замовчуванням 3000
    port: 81,
    // мережеві адреси, на яких відбувається прослуховування, за замовчуванням localhost: 127.0.0.1
    // 0.0.0.0 = усі мережеві адреси комп’ютера
    host: '0.0.0.0'
  }
}
  • рядок 5: встановлюємо базовий URL скомпільованого додатка;
  • рядки 8–14: визначаємо порт служби та мережеві адреси, на яких відбувається прослуховування;

Після цього відкриваємо термінал Laragon і переходимо до папки, що містить скомпільовану версію проєкту. Можна відкрити будь-який тип терміналу, але виконуваний файл [npm] має бути у каталозі терміналу. Це саме так у випадку з терміналом Laragon.

Після цього введіть команду [npm run start]:

Image

У [3] видно, що сервер запущено і він прослуховує на URL [http://192.168.1.128:81/nuxt-00/]. Тепер спробуємо звернутися до цього URL за допомогою браузера [4]. Ми бачимо те саме, що й раніше. На стороні терміналу були записані журнали [5]. Це журнал, розміщений у методі [created] сторінки [index.vue], який був виконаний сервером [node.js].

Image

На стороні браузера [6] також міститься журнал методу [created] зі сторінки [index.vue], але цього разу виконаного клієнтом.

3.11. Налаштування безпечного сервера

Вище URL додатка — це [http://192.168.1.128/nuxt-00/]. Ми хотіли б, щоб це було [https://192.168.1.128/nuxt-00/]. Тому нам потрібно створити захищений сервер. Ми покажемо, як це зробити.

Примітка: цей метод взято зі статті [https://stackoverflow.com/questions/56966137/how-to-run-nuxt-npm-run-dev-with-https-in-localhost].

Спочатку ми створюємо приватний та публічний ключі за допомогою команди [openssl]. [openssl] зазвичай встановлюється разом із сервером Laragon. Отже, ця команда доступна в будь-якому терміналі Laragon. Відкриємо термінал Laragon і перейдемо до папки розгорнутого додатка:

Image

Image

  • у [2] вводимо команду [openssl genrsa 2048 > server.key];
  • у [3] створюється файл [server.key];
  • у [4] вводиться команда [openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt];
  • у [5] створюється файл [server.crt];

Ці два файли складають самопідписаний сертифікат. Більшість браузерів приймають їх лише після схвалення користувачем, який завантажив сторінку.

Файли [server.key, server.crt] тепер мають використовуватися веб-додатком. Для цього файл [nuxt.config.js] слід змінити наступним чином:


import path from 'path'
import fs from 'fs'

export default {
  // маршрутизатор
  router: {
    // кореневий каталог URL додатка
    base: '/nuxt-00/'
  },
  // сервер
  server: {
    // порт служби, за замовчуванням 3000
    port: 81,
    // мережеві адреси, на яких відбувається прослуховування, за замовчуванням localhost: 127.0.0.1
    // 0.0.0.0 = усі мережеві адреси комп’ютера
    host: '0.0.0.0',
    // самопідписаний сертифікат
    https: {
      key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
      cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
    }
  }
}

Саме рядки 18–21 реалізують протокол [https].

Тепер запустимо додаток знову:

Image

3.12. Кінець першого прикладу

Перший приклад завершено. Він дав нам змогу ознайомитися з багатьма концепціями [nuxt]. Тепер ми розробимо інші приклади, які розмістимо в папках [nuxt-01, nuxt-02, ...]. Оскільки ці приклади використовуватимуть інший файл [nuxt.config.js], ми збережемо в кожній із цих папок файл [nuxt.config.js], який використовувався для їх виконання:

Image