3. Pierwsza aplikacja [nuxt.js]
3.1. Tworzenie aplikacji
W naszych pracach nad projektem [nuxt.js] nadal korzystamy z kodu VS. Utworzyliśmy pusty folder [dvp], w którym umieścimy nasze przykłady. Następnie otwieramy ten folder:

Zapisujemy obszar roboczy pod nazwą [intro-nuxtjs] [3-5]:

Otwieramy terminal o nazwie [6-7]:

Do tej pory korzystaliśmy z menedżera pakietów JavaScript o nazwie [npm]. Dla odmiany użyjemy tutaj menedżera [yarn]. Jest on zainstalowany, podobnie jak [npm], wraz z najnowszymi wersjami [node.js]. Aby utworzyć pierwszą aplikację [nuxt], używamy polecenia [yarn create nuxt-app <dossier>] [1]. Polecenie to zażąda pewnych informacji dotyczących generowanego projektu, a po ich uzyskaniu wygeneruje go: [2]:

W pliku [2] utworzono całą strukturę drzewa plików. Plik [package.json] zawiera listę bibliotek JavaScript pobranych do folderu [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"
}
}
Plik ten odzwierciedla odpowiedzi uzyskane w wyniku wykonania polecenia [create nuxt-app] w celu zdefiniowania utworzonego projektu (listopad 2019 r.). Czytelnik może posiadać inny plik [package.json]:
- mógł udzielić innych odpowiedzi na pytania;
- polecenie [create nuxt-app] uległo zmianie od czasu napisania tego dokumentu: zmieniły się zależności i wersje;
Wiersz 8 skryptu zawiera polecenie uruchamiające aplikację:

- w [4] widać, że aplikacja jest dostępna pod adresem URL [localhost:3000];
- w [5-6] widać, że aplikacja uruchamia serwer [6] oraz klienta (tego serwera) [5];
Wywołajmy adres URL [http://localhost:3000/] w przeglądarce:

3.2. Opis struktury drzewa aplikacji [nuxt]
Przyjrzyjmy się ponownie strukturze drzewa utworzonej aplikacji:

Rola folderów jest następująca:
assets | nieskompilowane zasoby aplikacji (obrazy itp.); |
static | pliki z tego katalogu będą dostępne w katalogu głównym aplikacji. W tym katalogu umieszcza się pliki, które powinny znajdować się w katalogu głównym aplikacji, na przykład plik [robots.txt] przeznaczony dla wyszukiwarek; |
components | komponenty aplikacji o nazwie [vue] wykorzystywane w plikach [layouts] i [pages]; |
układy | komponenty aplikacji o nazwie [vue], służące do tworzenia układów graficznych plików [pages]; |
strony | komponenty [vue] wyświetlane przez różne trasy aplikacji. Można je nazwać widokami aplikacji. Strony odgrywają szczególną rolę w [nuxt]: trasy są tworzone dynamicznie na podstawie drzewa katalogów znajdującego się w folderze [pages]; |
oprogramowanie pośredniczące | skrypty uruchamiane przy każdej zmianie trasy. Umożliwiają one kontrolowanie tych tras; |
wtyczki | nazwa tego folderu może wprowadzać w błąd. Może zawierać wtyczki, ale także zwykłe skrypty. Skrypty znajdujące się w tym folderze są uruchamiane podczas startu aplikacji; |
magazyn | jeśli zawiera skrypt [index.js], to definiuje on instancję magazynu [Vuex]; |
Jeśli folder jest pusty, można go usunąć z drzewa katalogów. Na powyższym przykładzie można usunąć foldery [assets, static, middleware, plugins, store] i [2].
3.3. Plik konfiguracyjny [nuxt.config]
Działanie aplikacji jest kontrolowane przez następujący plik [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: [
// Dokumentacja: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Dokumentacja: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Dokument: 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) {}
}
}
- wiersz 2: typ generowanej aplikacji:
- [universal]: aplikacja klient-serwer. Podczas pierwszego załadowania aplikacji, a także przy każdym odświeżeniu strony w przeglądarce, serwer jest proszony o dostarczenie strony;
- [sap]: aplikacja typu [Single Page Application]: serwer początkowo dostarcza całą aplikację. Następnie klient działa samodzielnie, nawet w przypadku odświeżenia strony w przeglądarce;
- wiersze 6–18: definiują nagłówek HTML <head> poszczególnych stron aplikacji:
- wiersz 7: tag <title> określający tytuł stron;
- wiersze 8–16: tagi <meta>;
- wiersz 17: tagi <link>
W wygenerowanej aplikacji tag <head> wygląda następująco (kod źródłowy strony wyświetlanej w przeglądarce):
<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">
Teraz zmodyfikujmy plik [nuxt.config] w następujący sposób:
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' }]
},
Po ponownym uruchomieniu aplikacji tag <head> przybrał następującą postać (kod źródłowy strony wyświetlanej w przeglądarce):
<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">
Wróćmy do pliku [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: [
// Dokumentacja: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Dokumentacja: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Dokumentacja: 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) {}
}
}
- wiersz 12: między każdą trasą klienta [nuxt] pojawia się pasek ładowania (loading), jeśli zmiana trasy zajmuje trochę czasu. Właściwość [loading] pozwala skonfigurować ten pasek ładowania, w tym przypadku jego kolor;
- wiersz 16: pliki globalne [css]. Zostaną one automatycznie dołączone do wszystkich stron aplikacji;
- wiersze 24–27: moduły JavaScript niezbędne do kompilacji (build) aplikacji;
- wiersze 31–36: moduły JavaScript używane przez aplikację;
- wiersz 41: konfiguracja biblioteki [axios], gdy została ona wybrana przez użytkownika dla okien dialogowych HTTP z serwerami zewnętrznymi;
- wiersze 45–50: ustawienia kompilacji (build) projektu;
Do pliku konfiguracyjnego można dodać inne klucze. Można w szczególności skonfigurować port usługi (domyślnie 3000) oraz katalog główny projektu (domyślnie katalog główny projektu). Właśnie to teraz zrobimy, dodając następujące klucze:
// katalog kodu źródłowego
srcDir: '.',
router: {
// URL katalog główny stron aplikacji
base: '/nuxt-intro/'
},
// serwer
server: {
// port usługi – domyślnie 3000
port: 81,
// adresy sieciowe, na których nasłuchuje – domyślnie localhost=127.0.0.1
host: '0.0.0.0'
}
- wiersz 2: lokalizacja kodu źródłowego projektu. Znajduje się on w bieżącym katalogu, tj. na tym samym poziomie co plik [nuxt.config.js]. Jest to wartość domyślna;
- wiersze 8–13: konfigurują serwer (należy pamiętać, że aplikacja typu [nuxt], taka jak [universal], jest zainstalowana zarówno na serwerze, jak i w przeglądarce klienta tego serwera);
- wiersz 10: strony aplikacji będą dostarczane na porcie 81 serwera;
- wiersz 12: domyślnie [localhost] (adres sieciowy 127.0.0.1). Urządzenie może mieć kilka adresów sieciowych, jeśli należy do kilku sieci. Adres 0.0.0.0 oznacza, że serwer WWW nasłuchuje na wszystkich adresach sieciowych urządzenia;
- wiersze 3–6: konfigurują router aplikacji [nuxt];
- wiersz 5: strony aplikacji będą dostępne pod adresami URL i [http://localhost:81/nuxt-intro/];
Dodajmy te linie do pliku [nuxt.config.js], a następnie uruchommy projekt (skrypt npm dev). Wynik jest następujący:

- w [1] – adres komputera w sieci publicznej;
- w pliku [2] – port usługi;
- w [3] – katalog główny aplikacji;
3.4. Katalog [layouts]

Folder [layouts] jest przeznaczony dla komponentów układu strony. Domyślnie używany jest komponent o nazwie [default.vue]. W tym projekcie jest to następujący komponent:
<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>
Komentarze
- wiersze 1–5: komponent [template];
- wiersz 3: tag <nuxt /> określa bieżącą stronę routingu;
- wiersze 7–55: styl osadzony przez komponent układu strony. Ponieważ zawiera on bieżącą stronę routingu, styl ten zostanie zastosowany do wszystkich stron routowanych w aplikacji;
Widać, że głównym celem strony [default.vue] jest tutaj zastosowanie stylu do stron objętych routingiem.
3.5. Folder [pages]

Folder [pages] zawiera przekierowane widoki, czyli te, które widzi użytkownik. Strona [index.vue] jest stroną główną aplikacji. W przypadku [nuxt.js] nie ma pliku routingu. Trasy są określane na podstawie struktury folderu [pages]. W tym przypadku obecność pliku [index.vue] spowoduje automatyczne utworzenie trasy o nazwie [index] oraz ścieżki [/index], która zostanie skrócona do [/], ponieważ jest to strona główna. W ten sposób tworzona jest następująca trasa:
Plik [index.vue] ma następującą treść:
<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>
W pliku [template] w wierszach 1–25 wyświetla się następujący widok:

Obraz [1] jest generowany przez wiersz 4 pliku [template]. Widać zatem, że strona wykorzystuje komponent o nazwie [logo]. Jest on zdefiniowany w wierszach 27–35 skryptu strony. W wierszu 28 oznaczenie [~] wskazuje katalog główny projektu.
3.6. Komponent [Logo]

Komponent [Logo.vue] ma następującą postać:
<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>
Komponent ten składa się głównie ze stylów i animacji służących do tworzenia animowanego obrazu.
3.7. Widok DevTools
[Vue DevTools] to rozszerzenie przeglądarki, które pozwala na przeglądanie obiektów [nuxt.js] i [vue.js] w przeglądarce. Korzystaliśmy z niej już w rozdziale poświęconym [vue.js]. Przyjrzyjmy się, co to narzędzie wykrywa po wyświetleniu strony głównej naszej aplikacji:

- w [1] komponent [PagesIndex] odnosi się do strony [pages/index.vue];
- W [2] widać, że ten komponent posiada właściwość [$route], która jest ścieżką prowadzącą do strony [index];
Jako proste ćwiczenie wyświetlmy tę ścieżkę w konsoli.
3.8. Modyfikacja strony głównej
Zmodyfikujemy plik [index.vue]. W naszej instalacji projektu zainstalowaliśmy dwie zależności:
- [eslint]: który sprawdza składnię plików JavaScript i komponentów Vue. Jeśli zainstalowano rozszerzenie [ESLint] do VSCode, składnia ta jest sprawdzana podczas wpisywania tekstu, a błędy są natychmiast sygnalizowane;
- [prettier]: formatuje kod JavaScript zgodnie ze standardami;
Te zależności są zapisane w pliku [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"
}
Zauważyłem (listopad 2019 r.), że po instalacji przeprowadzonej za pomocą polecenia [yarn create nuxt-app] narzędzia [eslint, prettier] nie działają podczas wpisywania tekstu. Błędy są zgłaszane dopiero podczas kompilacji. Po kilku poszukiwaniach znalazłem konfigurację, która działa:

W katalogu głównym projektu należy utworzyć folder o nazwie [.vscode], zawierający następujący plik [settings.json]:
{
"eslint.validate": [
{
"language": "vue",
"autoFix": true
},
{
"language": "javascript",
"autoFix": true
}
],
"eslint.autoFixOnSave": true,
"editor.formatOnSave": false
}
- wiersze 2–11: wskazują, że podczas sprawdzania poprawności plików .vue i .js program [eslint] musi poprawiać błędy, które jest w stanie poprawić;
- wiersz 12: podczas zapisywania pliku program [eslint] musi poprawiać błędy, które jest w stanie poprawić;
- wiersz 13: wyłącza domyślne formatowanie wykonywane przez VSCode podczas zapisywania. Zadanie to przejmie [prettier];
Dzięki tej konfiguracji:
- błędy składniowe lub formatowania są sygnalizowane już podczas wpisywania tekstu;
- błędy formatowania są automatycznie korygowane podczas zapisywania pliku;
Biblioteka [prettier] jest konfigurowana przez plik [.prettierrc]:

Domyślnie jest to następujący plik:
{
"semi": false,
"arrowParens": "always",
"singleQuote": true
}
- wiersz 1: brak znaku „;” na końcu instrukcji;
- wiersz 2: jeśli funkcja „strzałkowa” (arrow) ma jeden parametr, jest on ujęty w nawiasy;
- wiersz 3: ciągi znaków są ujęte w apostrofy (bez cudzysłowów);
Dodajemy dwie następujące reguły:
{
"semi": false,
"arrowParens": "always",
"singleQuote": true,
"printWidth": 120,
"endOfLine": "auto"
}
- wiersz 5: wiersz kodu może mieć maksymalnie 120 znaków;
- wiersz 6: znak końca wiersza może być dowolnie CRLF (Windows) lub LF (Unix);
Wreszcie plik [package.json] zostaje zmodyfikowany w następujący sposób:
"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 ."
},
- wiersz 7: dodajemy polecenie [lintfix], które jest identyczne z poleceniem [lint] z wiersza 6, z tą różnicą, że zawiera dodatkowo parametr [--fix]. Polecenie [lint] sprawdza składnię i format wszystkich plików w projekcie oraz zgłasza wszelkie błędy. [lintfix] wykona to samo, z tą różnicą, że problemy z formatowaniem, które można naprawić, zostaną automatycznie skorygowane. Polecenie [lintfix] należy użyć, jeśli kompilacja zakończy się niepowodzeniem z powodu problemów z formatowaniem plików;
Po wykonaniu tej czynności modyfikujemy plik [index.vue] w następujący sposób:

<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
},
// cykl życia
created() {
console.log('created, route=', this.$route)
}
}
</script>
- wiersze 10–12: dodajemy funkcję [created], która jest automatycznie uruchamiana po utworzeniu komponentu;
- wiersz 11: wyświetla się aktualną trasę;
- wiersz 2: komentarz przeznaczony dla funkcji [eslint]. Bez tego komentarza funkcja [eslint] zgłasza błąd; wiersz 11: funkcja [console] nie akceptuje instrukcji w funkcjach cyklu życia. [eslint] można konfigurować. Zachowamy jego domyślną konfigurację i będziemy używać komentarzy, takich jak ten w wierszu 2, aby wyłączyć konkretną regułę [eslint]. Będziemy używać dwóch rodzajów komentarzy:
- /* wyłączenie reguły [eslint] */: wyłączenie reguły dla całego pliku;
- // wyłączenie reguły [eslint]: wyłączenie reguły dla następującego wiersza;
Podczas wpisywania zgłaszane są błędy i dostępna jest funkcja [Quick Fix]:

Uruchamiamy projekt:

- w [1], zakładka [Vue] w narzędziach programistycznych przeglądarki (F12);
- w [2] i [3] – wyświetlanie trasy;
Dlaczego są dwa widoki, a nie jeden?
Aplikacja [nuxt] składa się z dwóch elementów: serwera i klienta:
- serwer dostarcza strony aplikacji przy jej uruchomieniu, a następnie za każdym razem, gdy strona jest odświeżana w przeglądarce (F5) lub gdy użytkownik ręcznie wpisuje adres URL aplikacji;
- każda strona dostarczana przez przeglądarkę zawiera żądaną stronę oraz kod JavaScript całej aplikacji, który jest następnie wykonywany w przeglądarce. To właśnie klient. Dopóki nie nastąpi odświeżenie strony w przeglądarce, aplikacja działa jak klasyczna aplikacja Vue w trybie [sap] (aplikacja jednostronicowa). Gdy tylko użytkownik ręcznie spowoduje odświeżenie strony, jest ona żądana od serwera i następuje powrót do poprzedniego etapu 1.
Należy zrozumieć, że są to te same strony z folderu [pages], które są dostarczane zarówno przez serwer, jak i przez klienta. Z tego powodu twórcy [nuxt] nazywają ten typ stron stronami izomorficznymi. Te same strony [.vue] mogą być interpretowane zarówno przez klienta, jak i przez serwer. Weźmy na przykład stronę [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
},
// cykl życia
created() {
console.log('created, route=', this.$route)
}
}
</script>
Ponieważ jest to strona główna, po uruchomieniu aplikacji jest ona dostarczana przez serwer. Strona na serwerze ma również cykl życia, taki sam jak w przypadku klasycznej strony [Vue], z wyjątkiem funkcji [beforeMount, monted], które nie istnieją po stronie serwera. Funkcja [created] jest wykonywana, co wyjaśnia pierwszy wpis w dzienniku. Oznacza to przy okazji, że serwer jest w stanie wykonywać skrypty JavaScript. W tym przypadku, jak i ogólnie, serwerem tym jest serwer [node.js]. Po utworzeniu strony na serwerze trafia ona do przeglądarki, gdzie ponownie przechodzi przez cykl życia. Funkcja [created] jest wykonywana po raz drugi, co powoduje wygenerowanie drugiego wpisu w dzienniku.
Architektura aplikacji [nuxt] mogłaby wyglądać następująco:

- [1]: przeglądarka, w której uruchomiona jest aplikacja [nuxt] po jej załadowaniu do przeglądarki. Nazywamy to klientem [nuxt];
- [3]: serwer, na którym początkowo hostowana jest aplikacja [nuxt]. Aplikacja ta jest ładowana do przeglądarki [1] przy uruchomieniu aplikacji oraz za każdym razem, gdy użytkownik odświeża bieżącą stronę w przeglądarce lub ręcznie wpisuje adres URL aplikacji. Na tym polega różnica w działaniu w porównaniu z klasyczną aplikacją Vue. W przypadku tej aplikacji, po załadowaniu do przeglądarki serwer nie był już nigdy więcej angażowany. Kolejną istotną różnicą, której na razie nie udało nam się dostrzec, jest to, że serwer aplikacji Vue jest serwerem statycznym, niezdolnym do interpretowania stron typu [.vue], podczas gdy serwer aplikacji Nuxt typu [universal] jest serwerem JavaScript. Przed wysłaniem strony do przeglądarki serwer może wykonać skrypty i na przykład pobrać dane z serwera [2];
- [2]: jest to serwer dostarczający dane albo do klientów [nuxt] i [1], albo do serwerów [nuxt] i [3];
Na powyższym schemacie można wyróżnić trzy podsystemy klient-serwer:
- [1, 3]: hostuje aplikację [nuxt]. [3] dostarcza ją przy uruchomieniu aplikacji wraz ze stroną startową oraz za każdym razem, gdy użytkownik ręcznie wywołuje stronę. [1] hostujeaplikację [nuxt] otrzymaną od [3], która działa wówczas w trybie [SAP], dopóki strony nie zostaną ręcznie wywołane w [3];
- [1, 2]: w trybie [SAP] klient [nuxt] pobiera dane zewnętrzne z jednego lub kilku serwerów;
- [3, 2]: podczas generowania strony żądanej przez użytkownika serwer [3] może również pobierać dane zewnętrzne z jednego lub kilku serwerów;
To właśnie serwer [3] odróżnia aplikację [nuxt] od aplikacji [vue]. Serwer ten jest angażowany za każdym razem, gdy użytkownik ręcznie żąda wyświetlenia strony. Obsługuje on te same strony [.vue], co klienci [vue] i [1]. Jest to serwer JavaScript zdolny do wykonywania skryptów obecnych na stronie. Może to na przykład zmienić sposób generowania strony głównej przy użyciu danych zewnętrznych: podczas gdy aplikacja [vue] pobiera je z konieczności od klienta [1], tutaj mogą one zostać pobrane przez serwer [3], zanim strona zostanie wysłana do klienta. Strona główna zyskuje w ten sposób na znaczeniu i może przyczynić się do poprawy wskaźnika SEO aplikacji.
Uwaga: w trybie programistycznym te trzy elementy [1, 2, 3] często znajdują się na tym samym komputerze. Tak będzie w przypadku wszystkich naszych przykładów.
3.9. Przeniesienie kodu źródłowego aplikacji do osobnego folderu
Następnie utworzymy różne aplikacje [nuxt] w tym samym folderze [dvp]. Faktem jest, że folder zależności [node_modules] generowany dla każdego projektu [nuxt] może zajmować kilkaset megabajtów. W folderze [dvp] utworzymy różne foldery [nuxt-00, nuxt-01, ...], w których umieścimy kod źródłowy przykładów do przetestowania. Następnie użyjemy pliku konfiguracyjnego [nuxt-config.js], aby wskazać lokalizację kodu źródłowego projektu [dvp], który pozostanie jedynym projektem [nuxt] w tym samouczku.
Przenosimy kod źródłowy aplikacji wygenerowanej początkowo za pomocą polecenia [yarn create nuxt-app] do folderu [nuxt-00]:

- w [2] przenieśliśmy foldery [components, layouts, pages] do folderu [nuxt-00];
- w [3] musimy zmodyfikować plik [nuxt.config.js];
Modyfikujemy plik [nuxt.config.js] w następujący sposób:
export default {
mode: 'universal',
/*
** Headers of the page
*/
...
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
// katalog kodu źródłowego
srcDir: 'nuxt-00',
// router
router: {
// katalog główny aplikacji URL
base: '/nuxt-00/'
},
// serwer
server: {
// port usługi, domyślnie 3000
port: 81,
// adresy sieciowe, na których nasłuchuje, domyślnie localhost: 127.0.0.1
// 0.0.0.0 = wszystkie adresy sieciowe komputera
host: '0.0.0.0'
}
}
Plik został zmodyfikowany w dwóch miejscach:
- wiersz 17: wskazujemy, że kod źródłowy projektu [dvp] znajduje się w folderze [nuxt-00];
- wiersz 21: wskazano, że katalogiem głównym aplikacji URL jest teraz [/nuxt-00/]. Ta zmiana nie była obowiązkowa. Można by pominąć tę właściwość, a wówczas katalogiem głównym URL byłby [/]. W tym przypadku pozwoli nam to zapamiętać, że wykonywany jest kod źródłowy z katalogu [nuxt-00];
Po wykonaniu tej czynności projekt [dvp] jest uruchamiany tak jak poprzednio:

3.10. Wdrożenie aplikacji [nuxt-00]
Uruchomimy aplikację [nuxt-00] w środowisku innym niż zintegrowane środowisko VSCode.
Najpierw skompilujemy aplikację:

- do [3], czyli wynik kompilacji klienta. Zostanie uruchomiony przez przeglądarkę;
- do [4], czyli wynik kompilacji po stronie serwera. Zostanie on uruchomiony przez serwer [node.js];
Wynik kompilacji jest umieszczany w folderze [.nuxt]:

Kopiujemy foldery [.nuxt, node_modules] i pliki [package.json, nuxt.config.js] do osobnego folderu:

Plik [package.json] został uproszczony w następujący sposób:
{
"scripts": {
"start": "nuxt start"
}
}
- zachowujemy jedynie skrypt [start], który umożliwia uruchomienie skompilowanej wersji projektu;
Plik [nuxt.config.js] zostaje uproszczony w następujący sposób:
export default {
// router
router: {
// katalog główny plików URL aplikacji
base: '/nuxt-00/'
},
// serwer
server: {
// port usługi, domyślnie 3000
port: 81,
// adresy sieciowe, na których nasłuchuje, domyślnie localhost: 127.0.0.1
// 0.0.0.0 = wszystkie adresy sieciowe komputera
host: '0.0.0.0'
}
}
- wiersz 5: ustalamy podstawowy plik URL skompilowanej aplikacji;
- wiersze 8–14: definiuje się port usługi oraz adresy sieciowe, na których aplikacja nasłuchuje;
Po wykonaniu tych czynności otwieramy terminal Laragon i przechodzimy do folderu zawierającego skompilowaną wersję projektu. Można otworzyć dowolny rodzaj terminala, ale plik wykonywalny [npm] musi znajdować się w katalogu roboczym terminala. Tak jest w przypadku terminala Laragon.
Następnie wpisujemy polecenie [npm run start]:

W [3] widać, że uruchomiono serwer, który nasłuchuje na porcie URL [http://192.168.1.128:81/nuxt-00/]. Teraz spróbujmy wywołać ten adres URL za pomocą przeglądarki [4]. Widzimy dokładnie to samo, co poprzednio. Po stronie terminala zapisano logi [5]. Jest to log umieszczony w metodzie [created] na stronie [index.vue], która została wykonana przez serwer [node.js].

Po stronie przeglądarki [6] znajduje się również log metody [created] ze strony [index.vue], ale tym razem wykonanej przez klienta.
3.11. Konfiguracja bezpiecznego serwera
Powyżej URL aplikacji to [http://192.168.1.128/nuxt-00/]. Chcielibyśmy, aby to było [https://192.168.1.128/nuxt-00/]. Musimy więc zbudować bezpieczny serwer. Pokażemy, jak to zrobić.
Uwaga: metoda została zaczerpnięta z artykułu [https://stackoverflow.com/questions/56966137/how-to-run-nuxt-npm-run-dev-with-https-in-localhost].
Najpierw tworzymy klucz prywatny i klucz publiczny za pomocą polecenia [openssl]. [openssl] jest zazwyczaj instalowany wraz z serwerem Laragon. W związku z tym polecenie to jest dostępne w każdym terminalu Laragon. Otwórzmy więc terminal Laragon i przejdźmy do folderu wdrożonej aplikacji:


- w [2] wpisujemy polecenie [openssl genrsa 2048 > server.key];
- w [3] tworzony jest plik [server.key];
- po wpisaniu [4] pojawia się polecenie [openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt];
- w pliku [5] tworzony jest plik [server.crt];
Te dwa pliki stanowią certyfikat z podpisem własnym. Większość przeglądarek akceptuje je dopiero po zatwierdzeniu przez użytkownika, który wywołał stronę.
Pliki [server.key, server.crt] muszą być teraz używane przez aplikację internetową. W tym celu plik [nuxt.config.js] należy zmodyfikować w następujący sposób:
import path from 'path'
import fs from 'fs'
export default {
// router
router: {
// katalog główny plików URL aplikacji
base: '/nuxt-00/'
},
// serwer
server: {
// port usługi, domyślnie 3000
port: 81,
// adresy sieciowe, na których nasłuchuje, domyślnie localhost: 127.0.0.1
// 0.0.0.0 = wszystkie adresy sieciowe komputera
host: '0.0.0.0',
// certyfikat z podpisem własnym
https: {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
}
}
}
Wiersze 18–21 wprowadzają protokół [https].
Teraz uruchommy ponownie aplikację:

3.12. Koniec pierwszego przykładu
Pierwszy przykład został zakończony. Pozwolił nam on zapoznać się z wieloma koncepcjami związanymi z [nuxt]. Teraz opracujemy kolejne przykłady, które umieścimy w folderach [nuxt-01, nuxt-02, ...]. Ponieważ przykłady te będą korzystać z innego pliku [nuxt.config.js], w każdym z tych folderów zapiszemy plik [nuxt.config.js], który posłużył do ich uruchomienia:
