5. Przykład [nuxt-02]: strony serwera i klienta
W tym projekcie pokazujemy:
- że strona zbudowana przez klienta może wizualnie różnić się od tej otrzymanej z serwera. Powoduje to szybką zmianę strony, zauważalną dla użytkownika, co negatywnie wpływa na ergonomię aplikacji. Jest to zatem opcja, której należy unikać;
- rozwiązanie, dzięki któremu strona kliencka odtwarza tę samą stronę, co ta wysłana przez serwer;
Projekt [nuxt-02] powstał początkowo poprzez skopiowanie projektu [nuxt-01].

Do projektu dodano folder [store] oraz dwie nowe strony. Wrócimy do tego później.
5.1. Strona [index]
5.1.1. Kod strony
Kod strony [index] wygląda następująco:
<!-- strona główna -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="warning"> Home - value= {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Home',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[home beforeCreate]')
},
created() {
// klient i serwer
console.log('[home created]')
// tylko serwer
if (process.server) {
this.value = 10
}
// klient i serwer
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[home beforeMount]')
},
mounted() {
// tylko klient
console.log('[home mounted]')
}
}
</script>
Komentarze
- wiersz 7: strona [index] wyświetli wartość swojej właściwości [value] (wiersz 28);
- wiersze 36–45: należy tutaj pamiętać, że funkcja [created] jest wykonywana zarówno po stronie serwera, jak i po stronie klienta. W wierszach 40–42 serwer ustawi wartość właściwości [value] na 10. Klient natomiast nie zmienia tej wartości. Chcemy po prostu sprawdzić, czy wartość ta jest zachowywana przez klienta. Okazuje się, że nie;
5.1.2. Wykonanie
Modyfikujemy plik [/nuxt.config.js], aby uruchomić projekt [nuxt-02]:
...
// katalog kodu źródłowego
srcDir: 'nuxt-02',
// router
router: {
// katalog główny aplikacji URL
base: '/nuxt-02/'
},
// 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: 'localhost'
}
...
Uruchamiamy projekt [1]:

Wyświetla się wówczas strona [index] [2-3]. Przez chwilę wyświetla ona wartość [10], a następnie wartość [0]. Co się stało?
Krok 1
Najpierw uruchamia się serwer. Wykonuje on kod strony [index]:
export default {
name: 'Home',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[home beforeCreate]')
},
created() {
// klient i serwer
console.log('[home created]')
// tylko serwer
if (process.server) {
this.value = 10
}
// klient i serwer
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[home beforeMount]')
},
mounted() {
// tylko klient
console.log('[home mounted]')
}
}
- z powodu wiersza 23 właściwość [value] z wiersza 10 przyjmuje wartość 10;
Można to sprawdzić, przeglądając kod źródłowy strony otrzymanej przez przeglądarkę (opcja [code source] w przeglądarce):
<!doctype html>
<html data-n-head-ssr>
<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">
<base href="/nuxt-02/">
<link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
....
</head>
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
<h4>[nuxt-02] : page serveur, page client</h4>
</div> <div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-02/" target="_self" class="nav-link active nuxt-link-active">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page1" target="_self" class="nav-link">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
</ul>
</div> <div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-warning">
Home - value= 10
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>window.__NUXT__ = ....;</script>
<script src="/nuxt-02/_nuxt/runtime.js" defer></script>
<script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
- wiersz 46: na otrzymanej stronie [value] miało wartość 10;
krok 2
Wiadomo, że po odebraniu strony skrypty z wierszy 57–60 przejmują kontrolę i zmieniają zachowanie otrzymanej strony, a nawet wyświetlane informacje, tak jak w tym przypadku. Skrypty te tworzą klienta, który również wykonuje kod strony [index], ten sam kod co serwer:
export default {
name: 'Home',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[home beforeCreate]')
},
created() {
// klient i serwer
console.log('[home created]')
// tylko serwer
if (process.server) {
this.value = 10
}
// klient i serwer
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[home beforeMount]')
},
mounted() {
// tylko klient
console.log('[home mounted]')
}
}
- aby zrozumieć, co się dzieje, należy zdać sobie sprawę, że klient [nuxt] nie wykona wierszy 22–24 (process.server=false);
- w klasycznej aplikacji [vue] właściwość [value] w wierszu 10 pozostaje na poziomie 0. Dlatego też, gdy klient przejdzie do otrzymanej strony, wyświetlana wartość zmienia się na [0];
Wartość wygenerowana przez serwer [nuxt] dla właściwości [value] nie miała żadnego znaczenia.
5.2. Strona [page1]
5.2.1. Sklep [Vuex]
Dodaliśmy folder [store] do projektu [nuxt-02]:

Obecność tego folderu powoduje, że [nuxt] automatycznie zaimplementuje magazyn [Vuex]. Plik [index.js] implementuje ten magazyn. W tym przypadku plik [index.js] ma następującą postać:
export const state = () => ({
counter: 0
})
export const mutations = {
increment(state, inc) {
state.counter += inc
}
}
[nuxt] implementuje magazyn [Vuex] na podstawie zawartości pliku [index.js]:
- wiersze 1–3: definicja stanu [state] magazynu. Stan ten jest zwracany przez funkcję. W tym przypadku stan ma tylko jedną właściwość – licznik w wierszu 2. Eksportowana funkcja musi nosić nazwę [state];
- wiersze 5–9: możliwe operacje na stanie sklepu. Nazywamy je [mutations]. W tym przypadku operacja [increment] pozwala zwiększyć wartość właściwości [counter] o wartość [inc]. Eksportowany obiekt musi nosić nazwę [mutations];
Widok [store] zaimplementowany przez [nuxt] jest dostępny w różnych miejscach. W widokach jest on dostępny we właściwości [this.$store].
5.2.2. Kod strony
Podobnie jak strona [index], strona [page1] wyświetli wartość licznika z magazynu Vuex:
<!-- strona 1 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="primary"> Page 1 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page1',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[home beforeCreate]')
},
created() {
// klient i serwer
console.log('[home created]')
// tylko serwer
if (process.server) {
this.$store.commit('increment', 25)
}
// klient i serwer
this.value = this.$store.state.counter
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[home beforeMount]')
},
mounted() {
// tylko klient
console.log('[home mounted]')
}
}
</script>
Komentarze
- wiersze 38–40: serwer zwiększy licznik o 25;
- wiersz 42: zarówno serwer, jak i klient wyświetlą wartość licznika;
- wiersz 7: wyświetlana jest wartość licznika;
Czytając ten kod, należy zrozumieć dwie rzeczy:
- kod wykonywany jest taki sam zarówno na serwerze, jak i na kliencie;
- Obiekt [this] nie jest ten sam: po stronie serwera występuje wersja [this], a po stronie klienta – wersja [client];
Chcemy ustalić, czy obiekt [this.$store] po stronie serwera jest taki sam jak obiekt [this.$store] po stronie klienta. Ponieważ to serwer uruchamia się jako pierwszy (podczas uruchamiania aplikacji), sprowadza się to do pytania: czy plik [store] zainicjowany przez serwer jest przekazywany do klienta?
5.2.3. Wykonanie
Uruchamiamy projekt [nuxt-02] i ręcznie wpisujemy adres [localhost:81/nuxt-02/page1], aby wywołać serwer. Podobnie jak podczas uruchamiania strony [index]:
- serwer uruchamia stronę [page1.vue];
- wysyła wygenerowaną stronę do przeglądarki. Strona ta zostaje wyświetlona;
- skrypty klienckie osadzone w wysłanej stronie przejmują kontrolę i ponownie uruchamiają stronę [page1.vue];
- wyświetlana strona zostaje następnie zmodyfikowana;
Ostateczny wynik jest następujący:

Tym razem wyświetlana wartość jest zgodna z wartością ustaloną przez serwer i wizualnie nie widać, aby strona „podskakiwała” z powodu zmiany przez klienta wartości wyświetlanej przez serwer. Co się tym razem stało?
Serwer wykonał następującą stronę: [page1]:
...
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page1',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page1 beforeCreate]')
},
created() {
// klient i serwer
console.log('[page1 created]')
// tylko serwer
if (process.server) {
this.$store.commit('increment', 25)
}
// klient i serwer
this.value = this.$store.state.counter
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page1 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page1 mounted]')
}
}
</script>
- wiersze 30–32 zostały wykonane bez błędów. Oznacza to, że również po stronie serwera [this.$store] odnosi się do zmiennej [Vuex]. Wiersz 31 ustawił licznik tej zmiennej na 25;
- po wykonaniu tej operacji strona została wysłana do klienta;
Jeśli przyjrzymy się stronie otrzymanej przez klienta, znajdziemy następujące elementy:
<!doctype html>
<html data-n-head-ssr>
<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">
<base href="/nuxt-02/">
<link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
...
</head>
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
<h4>[nuxt-02] : page serveur, page client</h4>
</div>
<div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-02/" target="_self" class="nav-link">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page1" target="_self" class="nav-link active nuxt-link-active">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
</ul>
</div> <div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-primary">
Page 1 - value = 25
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
window.__NUXT__ = (function (a, b, c) {
return {
layout: "default", data: [{}], error: null, state: { counter: 25 }, serverRendered: true,
logs: [
{ date: new Date(1574085336802), args: ["[home beforeCreate]"], type: a, level: b, tag: c },
{ date: new Date(1574085336839), args: ["[home created]"], type: a, level: b, tag: c },
{ date: new Date(1574085336869), args: ["value=", "25"], type: a, level: b, tag: c }
]
}
}("log", 2, ""));</script>
<script src="/nuxt-02/_nuxt/runtime.js" defer></script>
<script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
- wiersz 47: wartość wysłana przez serwer;
- wiersz 60: widać, że stan magazynu [Vuex] został osadzony na stronie. Umożliwi to klientowi, który uruchomi się po odebraniu strony, odtworzenie nowego magazynu [Vuex] z wartością 25 jako początkową wartością licznika;
Po odebraniu i wyświetleniu strony otrzymanej z serwera klient przejmuje kontrolę i z kolei uruchamia stronę [page1]:
...
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page1',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page1 beforeCreate]')
},
created() {
// klient i serwer
console.log('[page1 created]')
// tylko serwer
if (process.server) {
this.$store.commit('increment', 25)
}
// klient i serwer
this.value = this.$store.state.counter
console.log('value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page1 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page1 mounted]')
}
}
</script>
- wiersz 34: właściwość [value] z wiersza 18 otrzymuje z kolei wartość 25 z licznika;
Pamięć [nuxt] umożliwia zatem serwerowi przekazywanie informacji do klienta podczas początkowego ładowania strony, gdy jest ona wyszukiwana na serwerze. Należy pamiętać, że po pobraniu tej strony serwer nie jest już angażowany, a aplikacja działa jak klasyczna aplikacja [vue] w trybie SAP.
5.3. Strona [page2]
Na stronie [page2] pokazujemy inny sposób, dzięki któremu:
- serwer umieszcza na stronie obliczone informacje;
- klient nie modyfikuje tych informacji;
5.3.1. Kod strony
Kod strony [page2] zmienia się w następujący sposób:
<!-- strona 2 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page2',
// użyte komponenty
components: {
Layout,
Navigation
},
asyncData(context) {
// kto wykonuje ten kod?
console.log('asyncData, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zazwyczaj mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, wprowadzając sekundę oczekiwania
setTimeout(() => {
// ten wynik zostanie uwzględniony we właściwościach [data]
resolve({ value: 87 })
// log
console.log('asynData terminée')
}, 1000)
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page2 beforeCreate]')
},
created() {
// klient i serwer
console.log('[page2 created]')
},
beforeMount() {
// tylko klient
console.log('[page2 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page2 mounted]')
}
}
</script>
- wiersz 7: strona wyświetla wartość właściwości o nazwie [value];
- właściwość [value] nie istnieje jako element obiektu zwracanego przez funkcję [data]. W tym przypadku funkcja ta nie istnieje. Właściwość [value] jest tworzona dynamicznie w wierszu 36;
- wiersz 25: funkcja [asyncData] jest funkcją [nuxt]. Jak wskazuje jej nazwa, jest to zazwyczaj funkcja asynchroniczna. Jej typowym zadaniem jest pobieranie danych zewnętrznych. [nuxt] zapewnia, że strona nie zostanie wysłana do przeglądarki klienta, dopóki funkcja [asyncData] nie zwróci swoich danych asynchronicznych;
- funkcja [asyncData] otrzymuje jako parametr kontekst [nuxt]. Obiekt ten jest bardzo bogaty i zapewnia dostęp do wielu informacji o aplikacji [Nuxt]. Poznamy go w kolejnych sekcjach;
- wiersz 31: implementujemy funkcję [asyncData] z wykorzystaniem [Promise] (patrz dokument |Introduction au langage ECMASCRIPT 6 par l’exemple|). Konstruktor tej klasy przyjmuje jako parametr funkcję asynchroniczną, która:
- sygnalizuje powodzenie, zwracając dane za pomocą funkcji [resolve]. Obiekt zwrócony przez tę funkcję jest automatycznie dołączany do właściwości [data] strony;
- sygnalizuje niepowodzenie, zwracając błąd za pomocą funkcji [reject];
- wiersz 34: symulujemy funkcję asynchroniczną za pomocą funkcji [setTimeout]. Funkcja ta zwraca obiekt [{ value: 87 }] (wiersz 36) po upływie jednej sekundy (wiersz 31) dzięki funkcji [resolve], która sygnalizuje pomyślne wykonanie funkcji [Promise]. Obiekt zwrócony przez funkcję asynchroniczną jest automatycznie dołączany do właściwości [data] strony. I właśnie tę właściwość wyświetla wiersz 7;
- wiersz 27: przekonamy się, że funkcja [asyncData] jest wykonywana przez serwer, ale nie przez klienta;
- wiersz 29: inicjalizacja właściwości [value] odbywa się po stronie serwera;
Uwaga: obiekt [this] nie jest rozpoznawany w funkcji [asyncData], ponieważ obiekt enkapsulujący komponent [vue] nie został jeszcze utworzony;
5.3.2. Wykonanie
Uruchamiamy projekt [nuxt-02] i ręcznie wpisujemy adres [localhost:81/nuxt-02/page2], aby wywołać serwer. Podobnie jak podczas uruchamiania strony [index]:
- serwer uruchamia stronę [page2.vue];
- wysyła wygenerowaną stronę do przeglądarki. Strona ta zostaje wyświetlona;
- skrypty klienckie osadzone w wysłanej stronie przejmują kontrolę i ponownie uruchamiają stronę [page2.vue];
- wyświetlana strona zostaje następnie zmodyfikowana;
Ostateczny wynik jest następujący:

Tym razem wyświetlana wartość jest zgodna z wartością ustaloną przez serwer i wizualnie nie widać, aby strona „podskakiwała” z powodu zmiany przez klienta wartości wyświetlanej przez serwer. Co się tym razem stało?
Serwer wykonał następującą stronę [page2]:
<!-- strona 2 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page2',
// używane komponenty
components: {
Layout,
Navigation
},
asyncData(context) {
// kto wykonuje ten kod?
console.log('asyncData, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, czekając jedną sekundę
setTimeout(() => {
// wynik ten zostanie uwzględniony we właściwościach [data]
resolve({ value: 87 })
// log
console.log('asynData terminée')
}, 1000)
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page2 beforeCreate]')
},
created() {
// klient i serwer
console.log('[page2 created]')
},
beforeMount() {
// tylko klient
console.log('[page2 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page2 mounted]')
}
}
</script>
To właśnie wiersz 36 ustalił wartość wyświetlaną przez wiersz 7. Tak więc właśnie to otrzymała przeglądarka klienta. Dokładniej rzecz biorąc, otrzymała następującą stronę:
<!doctype html>
<html data-n-head-ssr>
<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">
<base href="/nuxt-02/">
<link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
...
</head>
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
<h4>[nuxt-02] : page serveur, page client</h4>
</div>
<div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-02/" target="_self" class="nav-link">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page1" target="_self" class="nav-link">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page2" target="_self" class="nav-link active nuxt-link-active">
Page 2
</a>
</li>
</ul>
</div>
<div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
Page 2 - value = 87
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
window.__NUXT__ = (function (a, b, c) {
return {
layout: "default", data: [{ value: 87 }], error: null, state: { counter: 0 }, serverRendered: true,
logs: [
{ date: new Date(1574096608555), args: ["asyncData, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
{ date: new Date(1574096608575), args: ["[page2 beforeCreate]"], type: a, level: b, tag: c },
{ date: new Date(1574096608599), args: ["[page2 created]"], type: a, level: b, tag: c }
]
}
}("log", 2, ""));</script>
<script src="/nuxt-02/_nuxt/runtime.js" defer></script>
<script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
- wiersz 48: widać, że wartość na otrzymanej stronie wynosi 87;
- wiersz 61: w odpowiedzi serwera widoczne są dwa obiekty: [data] i [state]:
- [state] to stan obiektu [Vuex]. Obiekt ten został utworzony na podstawie zawartości folderu [store] aplikacji [nuxt-02];
- [data] zawiera właściwości utworzone przez serwer za pomocą funkcji [asyncData]. Znajdujemy tu właściwość [value : 87] utworzoną przez serwer. Skrypty klienta włączą tę właściwość do właściwości strony [page2];
Wróćmy do kodu strony [page2]:
<!-- strona 2 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page2',
// używane komponenty
components: {
Layout,
Navigation
},
asyncData(context) {
// kto wykonuje ten kod?
console.log('asyncData, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, wprowadzając sekundę oczekiwania
setTimeout(() => {
// wynik ten zostanie uwzględniony we właściwościach [data]
resolve({ value: 87 })
// log
console.log('asynData terminée')
}, 1000)
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page2 beforeCreate]')
},
created() {
// klient i serwer
console.log('[page2 created]')
},
beforeMount() {
// tylko klient
console.log('[page2 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page2 mounted]')
}
}
</script>
- W wierszu 7 wykorzystano właściwość [value]. Strona nie definiuje jednak żadnej właściwości o nazwie [value]. Skrypty po stronie klienta automatycznie utworzyły tę właściwość na podstawie obiektu [data: [{ value: 87 }]] otrzymanego z serwera;
Z logów wynika ponadto, że funkcja [asyncData] nie została wykonana przez klienta:

Funkcja [asyncData] została wykonana przez serwer [1], ale nie przez klienta [2]. Ponadto należy zauważyć, że funkcje cyklu życia nie są wykonywane przez serwer przed zakończeniem funkcji [asyncData]. Aby to sprawdzić, można wydłużyć czas oczekiwania w ramach funkcji [asyncData].
5.4. Strona [page3]
Dodajemy do naszej aplikacji nową stronę [page3]:

5.4.1. Komponent [navigation]
Komponent [vavigation] został zmodyfikowany, aby umożliwić nawigację do nowej strony:
<template>
<!-- menu Bootstrap z trzema opcjami -->
<b-nav vertical>
<b-nav-item to="/" exact exact-active-class="active">
Home
</b-nav-item>
<b-nav-item to="/page1" exact exact-active-class="active">
Page 1
</b-nav-item>
<b-nav-item to="/page2" exact exact-active-class="active">
Page 2
</b-nav-item>
<b-nav-item to="/page3" exact exact-active-class="active">
Page 3
</b-nav-item>
</b-nav>
</template>
5.4.2. Kod komponentu [page3]
Kod strony [page3] jest następujący:
<!-- strona 3 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page3',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
fetch(context) {
// kto wykonuje ten kod?
console.log('fetch, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, czekając jedną sekundę
setTimeout(() => {
// powodzenie
resolve()
}, 1000)
}).then(() => {
// modyfikuje się zmienną
context.store.commit('increment', 28)
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page3 beforeCreate]')
},
created() {
// klient i serwer
this.value = this.$store.state.counter
console.log('[page3 created], value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page3 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page3 mounted]')
}
}
</script>
- wiersz 30: funkcja [fetch] zachowuje się analogicznie do funkcji [asyncData]:
- jest wykonywana przed funkcjami cyklu życia;
- obiekt [this] nie jest rozpoznawany w tej funkcji;
- działa asynchronicznie;
- cykl życia nie rozpoczyna się, dopóki funkcja asynchroniczna nie zwróci wyniku;
- wynik jest tutaj zwracany przez metodę [then] funkcji [Promise], wiersz 43;
- funkcja [fetch] otrzymuje parametr [context]. Parametr ten reprezentuje aktualny kontekst [nuxt];
- wiersz 30: wśród swoich licznych właściwości obiekt [context] posiada właściwość [store], która reprezentuje pasek narzędzi [Vuex] aplikacji;
- wiersz 41: sztucznie sygnalizuje się powodzenie operacji [Promise] po upływie jednej sekundy (por. dokument |Introduction au langage ECMASCRIPT 6 par l’exemple|);
- wiersz 45: następnie wykonywana jest metoda [then]. W jej ramach zwiększa się licznik [store];
5.4.3. Wykonanie
Uruchamiamy projekt [nuxt-02] i ręcznie wpisujemy adres [localhost:81/nuxt-02/page3], aby wysłać żądanie do serwera. Podobnie jak przy uruchomieniu strony [index]:
- serwer wygeneruje stronę [page3.vue];
- wysyła wygenerowaną stronę do przeglądarki. Strona ta zostaje wyświetlona;
- skrypty klienckie osadzone w wysłanej stronie przejmują kontrolę i ponownie uruchamiają stronę [page3.vue];
- wyświetlana strona zostaje następnie zmodyfikowana;
Ostateczny wynik jest następujący:

Wyświetlana wartość jest zgodna z wartością ustaloną przez serwer, a wizualnie nie widać, aby strona „podskakiwała” z powodu zmiany przez klienta wartości wyświetlanej przez serwer. Co się tym razem stało?
Serwer wykonał następującą stronę [page3]:
<!-- strona 3 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page3',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
fetch(context) {
// kto wykonuje ten kod?
console.log('fetch, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, czekając jedną sekundę
setTimeout(() => {
// powodzenie
resolve()
}, 1000)
}).then(() => {
// modyfikuje się zmienną store
context.store.commit('increment', 28)
// log
console.log('fetch commit terminé')
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page3 beforeCreate]')
},
created() {
// klient i serwer
this.value = this.$store.state.counter
console.log('[page3 created], value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page3 beforeMount]')
},
mounted() {
// tylko dla klientów
console.log('[page3 mounted]')
}
}
</script>
- wiersz 45: funkcja asynchroniczna [fetch] jest pierwszą z powyższych funkcji, która zostanie wykonana. Otrzymuje ona jako parametr obiekt o nazwie [context], który jest aktualnym kontekstem [nuxt]. Spośród bardzo wielu właściwości tego obiektu właściwość [context.store] reprezentuje pamięć tymczasową [Vuex];
- wiersz 45: w funkcji asynchronicznej [fetch] serwer ustawia licznik magazynu na 28;
- wiersz 56: gdy uruchamia się funkcja [created], [nuxt] gwarantuje, że funkcja asynchroniczna [fetch] zakończyła swoją pracę;
- wiersz 58: wartość licznika rolety jest przypisywana do właściwości [value] z wiersza 27;
- wiersz 7: wyświetlenie wartości [value], a więc licznika zmiennej;
Przeglądarka klienta otrzymuje następującą stronę:
<!doctype html>
<html data-n-head-ssr>
<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">
<base href="/nuxt-02/">
<link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
...
</head>
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
<h4>[nuxt-02] : page serveur, page client</h4>
</div>
<div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-02/" target="_self" class="nav-link">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page1" target="_self" class="nav-link">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
<li class="nav-item">
<a href="/nuxt-02/page3" target="_self" class="nav-link active nuxt-link-active">
Page 3
</a>
</li>
</ul>
</div> <div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
Page 3 - value = 28
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
window.__NUXT__ = (function (a, b, c) {
return {
layout: "default", data: [{}], error: null, state: { counter: 28 }, serverRendered: true,
logs: [
{ date: new Date(1574169916025), args: ["fetch, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
{ date: new Date(1574169917038), args: ["fetch commit terminé"], type: a, level: b, tag: c },
{ date: new Date(1574169917137), args: ["[page3 beforeCreate]"], type: a, level: b, tag: c },
{ date: new Date(1574169917167), args: ["[page3 created], value=", "28"], type: a, level: b, tag: c }
]
}
}("log", 2, ""));</script>
<script src="/nuxt-02/_nuxt/runtime.js" defer></script>
<script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
- wiersz 52: widać, że wartość na otrzymanej stronie wynosi 28;
- wiersz 65: w odpowiedzi serwera widać, że serwer wysłał do klienta stan [state] magazynu [Vuex]. Dzięki tej informacji skrypty klienckie będą mogły odtworzyć magazyn [Vuex];
Skrypty klienckie z kolei wykonają kod strony [page3]:
<!-- strona 3 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page3',
// używane komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
fetch(context) {
// kto wykonuje ten kod?
console.log('fetch, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, czekając jedną sekundę
setTimeout(() => {
// powodzenie
resolve()
}, 1000)
}).then(() => {
// modyfikuje się zmienną store
context.store.commit('increment', 28)
// log
console.log('fetch commit terminé')
})
}
},
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[page3 beforeCreate]')
},
created() {
// klient i serwer
this.value = this.$store.state.counter
console.log('[page3 created], value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page3 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page3 mounted]')
}
}
</script>
- wiersz 58: funkcja [created] wykonywana przez klienta przypisuje wartość licznika do właściwości [value] w wierszu 27;
- w wierszu 7 wyświetlana jest ta wartość. Ponieważ jest to ta sama wartość, co ta wysłana przez serwer, nie widać, aby strona „podskakiwała” w wyniku zmiany;
Z logów wynika ponadto, że funkcja [fetch] nie została wykonana przez klienta:

Funkcja [fetch] została wykonana przez serwer [1], ale nie przez klienta [2]. Ponadto można zauważyć, że funkcje cyklu życia nie są wykonywane przez serwer przed zakończeniem funkcji [fetch] i [3]. Aby to sprawdzić, można wydłużyć czas oczekiwania w ramach funkcji [fetch].
Strony [page1] i [page3] przedstawiły dwie metody wykorzystujące pamięć tymczasową [Vuex] do przekazywania informacji z serwera do klienta. Można zadać sobie pytanie, czy są one równoważne. Aby to sprawdzić, tworzymy stronę [page4].
5.5. Strona [page4]
Dodajemy nową stronę [page4] do naszej aplikacji:

5.5.1. Komponent [navigation]
Komponent [navigation] został zmodyfikowany, aby umożliwić nawigację do nowej strony:
<template>
<!-- menu Bootstrap z pięcioma opcjami -->
<b-nav vertical>
<b-nav-item to="/" exact exact-active-class="active">
Home
</b-nav-item>
<b-nav-item to="/page1" exact exact-active-class="active">
Page 1
</b-nav-item>
<b-nav-item to="/page2" exact exact-active-class="active">
Page 2
</b-nav-item>
<b-nav-item to="/page3" exact exact-active-class="active">
Page 3
</b-nav-item>
<b-nav-item to="/page4" exact exact-active-class="active">
Page 4
</b-nav-item>
</b-nav>
</template>
5.5.2. Kod komponentu [page4]
Kod strony [page4] jest następujący:
<!-- strona 4 -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Page 4 - value = {{ value }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Page4',
// użyte komponenty
components: {
Layout,
Navigation
},
data() {
return {
value: 0
}
},
// cykl życia
async beforeCreate() {
// klient i serwer
console.log('[page4 beforeCreate]')
// tylko dla serwera
if (process.server) {
// wykonuje się funkcję asynchroniczną
const valeur = await new Promise(function(resolve, reject) {
// zwykle mamy tu do czynienia z funkcją asynchroniczną
// symuluje się ją z 10-sekundowym oczekiwaniem
setTimeout(() => {
// powodzenie – zwracamy wartość licznika
resolve(52)
}, 10000)
})
// modyfikuje się zmienną przechowującą
this.$store.commit('increment', valeur)
// log
console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
}
},
created() {
// klient i serwer
this.value = this.$store.state.counter
console.log('[page4 created], value=', this.value)
},
beforeMount() {
// tylko klient
console.log('[page4 beforeMount]')
},
mounted() {
// tylko klient
console.log('[page4 mounted]')
}
}
</script>
- wiersz 30: to, co wcześniej było wykonywane w funkcji [fetch], jest teraz wykonywane w metodzie [beforeCreate]. Używa się pary async (wiersz 30) / await (wiersz 36), aby poczekać na zakończenie funkcji asynchronicznej;
- wiersz 36: wynik funkcji asynchronicznej, zwrócony w wierszu 41, jest pobierany po upływie 10 sekund (wiersz 42);
- wiersze 50–54: w metodzie [created], wykonywanej zarówno po stronie serwera, jak i po stronie klienta, licznik jest przypisywany do właściwości [value] strony;
5.5.3. Wykonanie
Uruchamiamy projekt [nuxt-02] i ręcznie wpisujemy adres [localhost:81/nuxt-02/page4], aby wysłać żądanie do serwera. Podobnie jak przy uruchomieniu strony [index]:
- serwer uruchamia stronę [page4.vue];
- wysyła wygenerowaną stronę do przeglądarki. Strona ta zostaje wyświetlona;
- skrypty klienckie osadzone w wysłanej stronie przejmują kontrolę i ponownie uruchamiają stronę [page4.vue];
- wyświetlana strona zostaje następnie zmodyfikowana;
Ostateczny wynik jest następujący:

Wbrew oczekiwaniom wartość wyświetlana w [2] nie wynosi 52. Co się stało?
Oto zapisy w logach:

Można zauważyć, że w przypadku [1] nie wyświetlono logu zakończenia operacji asynchronicznej. Funkcja [created], która wyświetla wartość licznika, pokazuje 0. Wszystko to sugeruje, że [nuxt] nie doczekało się zakończenia operacji asynchronicznej.
Jeśli wrócimy do terminala funkcji VSCode, która służyła do uruchomienia aplikacji, znajdziemy tam logi funkcji [3-4]. Widać, że funkcja asynchroniczna została rzeczywiście wykonana po stronie serwera.
Ostatecznie funkcja [beforeCreate] została w całości wykonana po stronie serwera, ale [nuxt] nie poczekała na zakończenie jej wykonywania, aby wysłać stronę do przeglądarki klienta, podczas gdy czeka na zakończenie funkcji [fetch]. Należy zatem zastosować tę metodę, jeśli chcemy, aby serwer zainicjował magazyn [Vuex].
5.6. Nawigacja w aplikacji [vue]
Pokazaliśmy, co się dzieje, gdy każda ze stron [index, page1, page2, page3, page4] jest początkowo ładowana przez serwer. W praktyce tak się nie dzieje: podczas normalnego działania na serwerze wyszukiwana jest tylko strona [index]. Przyjrzyjmy się w tym przypadku trzem stronom:
strona [index]

Wyjaśniliśmy już ten wynik w akapicie dotyczącym linków.
Teraz kliknijmy na link [Page 1]:

Wyświetlana wartość to 0. Wynosiła ona 25, gdy strona została po raz pierwszy zażądana od serwera poprzez ręczne wpisanie jej adresu URL. Wyjaśnienie jest proste. Wykonany kod wygląda następująco:
created() {
// klient i serwer
console.log('[page1 created]')
// tylko serwer
if (process.server) {
this.$store.commit('increment', 25)
}
// klient i serwer
this.value = this.$store.state.counter
console.log('value=', this.value)
},
To właśnie wiersz 6 ustawiał licznik na 25. Ponieważ strona nie została wywołana z serwera, wiersze 5–7 nie zostały wykonane, a licznik w zmiennej [Vuex] pozostał na poziomie 0.
Teraz kliknijmy link [Page 2]:

Tym razem nie wyświetla się żadna wartość, a ponadto w logach konsoli pojawia się ostrzeżenie:

- W przypadku [1] okazuje się, że funkcja [asyncData] została wykonana przez klienta. Zawsze jest tak:
- jest ona wykonywana przez serwer, jeśli strona jest żądana od serwera. W takim przypadku nie jest ona wykonywana przez klienta;
- a następnie za każdym razem, gdy strona jest celem bieżącej trasy klienta;
- w przypadku [2]: funkcja [nuxt] generuje ostrzeżenie, ponieważ w szablonie strony znajduje się wyrażenie reaktywne {{ value }}, podczas gdy strona nie posiada właściwości [value];
Przypomnijmy kod wykonywany przez klienta:
asyncData() {
// kto wykonuje ten kod?
console.log('asyncData, client=', process.client, 'serveur=', process.server)
// tylko dla serwera
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zwykle mamy tu funkcję asynchroniczną – ale nie w tym przypadku
// ten wynik zostanie uwzględniony we właściwościach [data]
resolve({ value: 87 })
})
}
},
- w wierszu 3 widać, że funkcja [asyncData] została wykonana przez klienta jeszcze przed funkcjami cyklu życia;
- wiersz 5 uniemożliwił wykonanie pozostałej części kodu, który tworzył właściwość [value], ponieważ kod ten jest wykonywany przez klienta;
Przejdźmy teraz do strony [page3]:

- w [2] mieliśmy wartość 28, gdy strona została dostarczona przez serwer. Tutaj tak nie jest;
- w [4] widać, że funkcja [fetch] została wykonana po stronie klienta;
Przyjrzyjmy się kodowi wykonanemu przez klienta:
...
fetch(context) {
// kto wykonuje ten kod?
console.log('fetch, client=', process.client, 'serveur=', process.server)
// tylko na serwerze
if (process.server) {
// zwracamy obietnicę
return new Promise(function(resolve, reject) {
// zazwyczaj mamy tu do czynienia z funkcją asynchroniczną
// symulujemy ją, czekając jedną sekundę
setTimeout(() => {
// powodzenie
resolve()
}, 1000)
}).then(() => {
// modyfikuje się zmienną store
context.store.commit('increment', 28)
// log
console.log('fetch commit terminé')
})
}
},
...
- klient wykonuje metodę [fetch], wiersz 2;
- wiersze 6–21 nie są wykonywane, ponieważ warunek [process.server] jest fałszywy. W związku z tym wiersz 17, który ustawia licznik na 28, nie jest wykonywany. Licznik pozostaje na zero. Dlatego klient wyświetla 0 zamiast 28;
Przejdźmy teraz do strony [page4]. Otrzymujemy następujący wynik:

- w [2] – wartość licznika;
- w [3] – logi klienta;
Kod wykonywany przez klienta jest następujący:
...
data() {
return {
value: 0
}
},
// cykl życia
async beforeCreate() {
// klient i serwer
console.log('[page4 beforeCreate]')
// tylko dla serwera
if (process.server) {
// wykonuje się funkcję asynchroniczną
const valeur = await new Promise(function(resolve, reject) {
// zazwyczaj mamy tu do czynienia z funkcją asynchroniczną
// symuluje się ją z 10-sekundowym oczekiwaniem
setTimeout(() => {
// powodzenie – zwracamy wartość licznika
resolve(52)
}, 10000)
})
// modyfikuje się zmienną
this.$store.commit('increment', valeur)
// log
console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
}
},
created() {
// klient i serwer
this.value = this.$store.state.counter
console.log('[page4 created], value=', this.value)
},
...
- wiersze 12–26 nie są wykonywane przez klienta, ponieważ warunek [process.server] w wierszu 12 jest fałszywy. Zatem licznik w zmiennej [Vuex] ma wartość 0 (jego wartość początkowa w zmiennej) i to właśnie ta wartość jest wyświetlana na stronie;
Można się zastanawiać, co się stanie, gdy skomentujemy warunek [if] w wierszach 12 i 26. Oto odpowiedź:
- klient wykonuje tym razem linie 14–25, ale [nuxt] nie czeka na zakończenie funkcji asynchronicznej (tak jak w przypadku serwera) i pozostawia licznik na 0;
- po 10 sekundach funkcja asynchroniczna kończy działanie, a licznik w wierszu 23 zostaje ustawiony na 52;
- gdy ponownie przejdzie się do strony [page4], wyświetlana jest wówczas wartość 52;
5.7. Résumé
Z naszych różnych testów można wyciągnąć następujące wnioski:
- jeśli strona [index] ma zawierać dane zewnętrzne, serwer może je pobrać za pomocą funkcji [asyncData];
- jeśli serwer ma zainicjować pamięć [Vuex] danymi zewnętrznymi podczas ładowania strony [index], zrobi to w funkcji [fetch];
- strona wygenerowana przez serwer i strona wygenerowana przez klienta muszą być identyczne, jeśli chcemy uniknąć efektu „podskakiwania” spowodowanego tym, że strona klienta wizualnie zastępuje stronę wysłaną przez serwer i pierwotnie wyświetloną;
Poznamy inne aspekty funkcji [nuxt] na podstawie nowego przykładu.