Tworzenie renderowanych aplikacji Vue.js po stronie serwera przy użyciu Nuxt.js

Opublikowany: 2022-03-11

Struktury/biblioteki JavaScript, takie jak Vue, mogą zapewnić fantastyczne wrażenia użytkownika podczas przeglądania witryny. Większość oferuje sposób na dynamiczną zmianę zawartości strony bez konieczności każdorazowego wysyłania żądania do serwera.

Jest jednak pewien problem z tym podejściem. Podczas początkowego ładowania witryny przeglądarka nie otrzymuje pełnej strony do wyświetlenia. Zamiast tego zostaje wysłany zestaw elementów do skonstruowania strony (HTML, CSS, inne pliki) i instrukcje, jak je połączyć (struktura/biblioteka JavaScript). Zebranie wszystkich tych informacji zajmuje wymierną ilość czasu zanim Twoja przeglądarka będzie miała coś do wyświetlenia. To tak, jakby wysłać kilka książek wraz z regałem na książki. Trzeba by najpierw zbudować regał, a potem wypełnić go książkami.

Rozwiązanie tego jest sprytne: miej na serwerze wersję frameworka/biblioteki, która może zbudować stronę gotową do wyświetlenia. Następnie wyślij tę kompletną stronę do przeglądarki wraz z możliwością wprowadzania dalszych zmian i nadal ma dynamiczną zawartość strony (framework/bibliotekę), tak jak wysyłanie gotowej biblioteczki wraz z niektórymi książkami. Jasne, nadal musisz odłożyć książki do biblioteczki, ale od razu masz coś nadającego się do użytku.

Wizualne porównanie renderowania po stronie klienta i po stronie serwera

Poza tą głupią analogią jest jeszcze kilka innych zalet. Na przykład strona, która rzadko się zmienia, taka jak strona O nas, nie musi być odtwarzana za każdym razem, gdy użytkownik o to poprosi. Tak więc serwer może go utworzyć raz, a następnie buforować lub przechowywać gdzieś do wykorzystania w przyszłości. Tego rodzaju ulepszenia prędkości mogą wydawać się niewielkie, ale w środowisku, w którym czas reakcji jest mierzony w milisekundach (lub mniej), liczy się każdy najmniejszy bit.

Jeśli chcesz uzyskać więcej informacji na temat zalet SSR w środowisku Vue, zapoznaj się z własnym artykułem Vue na temat SSR. Istnieje wiele możliwości osiągnięcia tych wyników, ale najpopularniejszą, polecaną również przez zespół Vue, jest Nuxt.

Dlaczego Nuxt.js

Nuxt.js jest oparty na implementacji SSR dla popularnej biblioteki React o nazwie Next. Po zapoznaniu się z zaletami tego projektu zaprojektowano podobną implementację dla Vue o nazwie Nuxt. Osoby zaznajomione z kombinacją React+Next zauważą wiele podobieństw w projekcie i układzie aplikacji. Jednak Nuxt oferuje funkcje specyficzne dla Vue, aby stworzyć potężne, ale elastyczne rozwiązanie SSR dla Vue.

Nuxt został zaktualizowany do wersji 1.0 gotowej do produkcji w styczniu 2018 roku i jest częścią aktywnej i dobrze wspieranej społeczności. Jedną ze wspaniałych rzeczy jest to, że budowanie projektu za pomocą Nuxt nie różni się tak bardzo od budowania jakiegokolwiek innego projektu Vue. W rzeczywistości zapewnia szereg funkcji, które umożliwiają tworzenie dobrze ustrukturyzowanych baz kodu w krótszym czasie.

Inną ważną rzeczą, na którą należy zwrócić uwagę, jest to , że Nuxt nie musi być używany do SSR . Jest promowany jako platforma do tworzenia uniwersalnych aplikacji Vue.js i zawiera polecenie ( nuxt generate ) do tworzenia statycznie generowanych aplikacji Vue przy użyciu tej samej bazy kodu. Więc jeśli obawiasz się zanurzenia w głąb SSR, nie panikuj. Zawsze możesz utworzyć witrynę statyczną, jednocześnie korzystając z funkcji Nuxt.

Aby uchwycić potencjał Nuxt, stwórzmy prosty projekt. Ostateczny kod źródłowy tego projektu jest hostowany na GitHub, jeśli chcesz go zobaczyć, lub możesz wyświetlić wersję na żywo utworzoną za pomocą nuxt generate i hostowaną na Netlify.

Tworzenie projektu Nuxt

Na początek użyjmy generatora projektów Vue o nazwie vue-cli , aby szybko utworzyć przykładowy projekt:

 # install vue-cli globally npm install -g vue-cli # create a project using a nuxt template vue init nuxt-community/starter-template my-nuxt-project

Po przejściu przez kilka opcji, utworzy to projekt w folderze my-nuxt-project lub cokolwiek, co określiłeś. Następnie wystarczy zainstalować zależności i uruchomić serwer:

 cd my-nuxt-project npm install # Or yarn npm run dev

No to jedziemy. Otwórz przeglądarkę na localhost:3000 i Twój projekt powinien działać. Niewiele różni się od tworzenia projektu Vue Webpack. Jednak gdy spojrzymy na rzeczywistą strukturę aplikacji, nie ma jej zbyt wiele, zwłaszcza w porównaniu z czymś w rodzaju szablonu Vue Webpack.

Schemat katalogów projektów i ich związek z plikiem konfiguracyjnym Nuxt

Zajrzenie do package.json również pokazuje, że mamy tylko jedną zależność, sam Nuxt. Dzieje się tak, ponieważ każda wersja Nuxt jest dostosowana do pracy z określonymi wersjami Vue, Vue-router i Vuex i łączy je wszystkie razem.

W katalogu głównym projektu znajduje się również plik nuxt.config.js . Pozwala to dostosować szereg funkcji oferowanych przez Nuxt. Domyślnie ustawia dla Ciebie tagi nagłówka, kolor paska ładowania i reguły ESLint. Jeśli chcesz zobaczyć, co możesz skonfigurować, oto dokumentacja; omówimy niektóre opcje w tym artykule.

Więc co jest takiego specjalnego w tych katalogach?

Układ projektu

Jeśli przeglądasz utworzone katalogi, wszystkie z nich mają towarzyszący plik Readme z krótkim podsumowaniem tego, co znajduje się w tym katalogu i często łączem do dokumentacji.

Jest to jedna z zalet korzystania z Nuxt: domyślna struktura aplikacji. Każdy dobry programista front-end utworzy podobną strukturę aplikacji, ale istnieje wiele różnych pomysłów na struktury, a podczas pracy w zespole, nieuchronnie poświęci się trochę czasu na dyskusję lub wybór tej struktury. Nuxt zapewnia jeden dla Ciebie.

Nuxt wyszuka określone katalogi i zbuduje dla Ciebie aplikację w zależności od tego, co znajdzie. Przeanalizujmy te katalogi jeden po drugim.

Strony

To jedyny wymagany katalog. Wszystkie komponenty Vue w tym katalogu są automatycznie dodawane do vue-router na podstawie ich nazw plików i struktury katalogów. Jest to niezwykle wygodne. Normalnie i tak miałbym osobny katalog Pages i musiałbym ręcznie zarejestrować każdy z tych komponentów w innym pliku routera. Ten plik routera może stać się skomplikowany w przypadku większych projektów i może wymagać podziału w celu zachowania czytelności. Zamiast tego Nuxt zajmie się całą tą logiką za Ciebie.

Aby to zademonstrować, możemy stworzyć komponent Vue o nazwie about.vue w katalogu Pages. Dodajmy po prostu prosty szablon, taki jak:

 <template> <h1>About Page</h1> </template>

Gdy zapiszesz, Nuxt ponownie wygeneruje dla Ciebie trasy. Widząc, jak nazwaliśmy nasz komponent about.vue , jeśli przejdziesz do /about , powinieneś zobaczyć ten komponent. Prosty.

Jest jedna nazwa pliku, która jest wyjątkowa. Nazwanie pliku index.vue utworzy ścieżkę główną dla tego katalogu. Gdy projekt jest generowany, w katalogu stron znajduje się już komponent index.vue , który jest powiązany ze stroną główną lub stroną docelową Twojej witryny. (W przykładzie deweloperskim będzie to po prostu localhost:3000 .)

Nuxt skanuje pliki Vue w katalogu stron i wyświetla odpowiednie strony.

A co z głębszymi trasami? Podkatalogi w katalogu Pages pomagają uporządkować trasy. Jeśli więc chcielibyśmy mieć stronę Wyświetl produkt, moglibyśmy ustrukturyzować nasz katalog stron mniej więcej tak:

 /pages --| /products ----| index.vue ----| view.vue

Teraz, jeśli przejdziemy do /products/view , zobaczymy komponent view.vue w katalogu products. Jeśli zamiast tego przejdziemy do /products , zobaczymy komponent index.vue w katalogu products.

Możesz zapytać, dlaczego nie utworzyliśmy komponentu products.vue w katalogu stron zamiast tego, co zrobiliśmy dla strony /about . Możesz pomyśleć, że wynik byłby taki sam, ale jest różnica między tymi dwiema strukturami. Zademonstrujmy to, dodając kolejną nową stronę.

Powiedzmy, że chcieliśmy osobnej strony Informacje dla każdego pracownika. Na przykład utwórzmy dla mnie stronę Informacje. Powinien znajdować się w /about/ben-jones . Początkowo możemy spróbować uporządkować katalog Pages w ten sposób:

 /pages --| about.vue --| /about ----| ben-jones.vue

Kiedy próbujemy uzyskać dostęp do /about/ben-jones , zamiast tego otrzymujemy komponent about.vue , taki sam jak /about . Co tu się dzieje?

Co ciekawe, to, co robi tutaj Nuxt, to generowanie zagnieżdżonej trasy . Ta struktura sugeruje, że chcesz mieć stałą trasę /about , a wszystko w obrębie tej trasy powinno być zagnieżdżone we własnym obszarze widoku. W vue-router oznaczałoby to określenie komponentu <router-view /> wewnątrz komponentu about.vue . W Nuxt jest to ta sama koncepcja, z wyjątkiem tego, że zamiast <router-view /> po prostu używamy <nuxt /> . Zaktualizujmy więc nasz komponent about.vue , aby umożliwić zagnieżdżone trasy:

 <template> <div> <h1>About Page</h1> <nuxt /> </div> </template>

Teraz, gdy przechodzimy do /about , otrzymujemy komponent about.vue , który mieliśmy wcześniej, tylko z tytułem. Jednak gdy przechodzimy do /about/ben-jones , zamiast tego mamy renderowany tytuł i komponent ben-jones.vue w miejscu symbolu zastępczego <nuxt/> .

Nie tego początkowo chcieliśmy, ale pomysł posiadania strony Informacje z listą osób, które po kliknięciu wypełniają sekcję na stronie ich informacjami, jest ciekawym pomysłem, więc zostawmy to na razie . Jeśli chciałbyś mieć inną opcję, wszystko, co zrobilibyśmy, to zrestrukturyzować nasze katalogi. Wystarczyłoby przenieść komponent about.vue do katalogu /about i zmienić jego nazwę na index.vue , tak aby powstała struktura wyglądałaby następująco:

 /pages --| /about ----| index.vue ----| ben-jones.vue

Na koniec powiedzmy, że chcieliśmy użyć parametrów trasy do pobrania określonego produktu. Na przykład chcemy móc edytować produkt, przechodząc do /products/edit/64 , gdzie 64 to product_id . Możemy to zrobić w następujący sposób:

 /pages --| /products ----| /edit ------| _product_id.vue

Zwróć uwagę na podkreślenie na początku komponentu _product_id.vue — oznacza to parametr trasy, który jest następnie dostępny w obiekcie $route.params lub w obiekcie params w Kontekście Nuxta (więcej o tym później). Zwróć uwagę, że kluczem dla parametru będzie nazwa komponentu bez początkowego podkreślenia — w tym przypadku product_id — więc staraj się, aby były unikatowe w ramach projektu. W rezultacie w _product_id.vue możemy mieć coś takiego:

 <template> <h1>Editing Product {{ $route.params.product_id }}</h1> </template>

Możesz zacząć wyobrażać sobie bardziej złożone układy, których konfiguracja za pomocą vue-router byłaby uciążliwa. Na przykład możemy połączyć wszystkie powyższe w trasę taką jak:

 /pages --| /categories ----| /_category_id ------| products.vue ------| /products --------| _product_id.vue

Nie jest trudno wytłumaczyć, co wyświetliłoby się w /categories/2/products/3 . Otrzymalibyśmy komponent products.vue z zagnieżdżonym komponentem _product_id.vue z dwoma parametrami trasy: category_id i product_id . Jest to o wiele prostsze do uzasadnienia niż równoważna konfiguracja routera.

Skoro już jesteśmy przy temacie, jedną z rzeczy, które zwykle robię w konfiguracji routera, jest ustawienie strażników routera. Ponieważ Nuxt buduje dla nas router, można to zrobić na samym komponencie za pomocą beforeRouteEnter . Jeśli chcesz sprawdzić poprawność parametrów trasy, Nuxt udostępnia metodę komponentową o nazwie validate . Jeśli więc chcesz sprawdzić, czy product_id jest liczbą przed próbą renderowania komponentu, dodaj następujące elementy do tagu skryptu _product_id.vue :

 export default { validate ({ params }) { // Must be a number return /^\d+$/.test(params.product_id) } }

Teraz przejście do /categories/2/products/someproduct powoduje wyświetlenie błędu 404, ponieważ someproduct nie jest prawidłowym numerem.

To tyle, jeśli chodzi o katalog Pages. Nauka prawidłowej struktury tras w tym katalogu jest niezbędna, więc poświęcenie trochę czasu na początku jest ważne, aby jak najlepiej wykorzystać Nuxt. Jeśli szukasz krótkiego przeglądu, zawsze warto zapoznać się z dokumentacją dotyczącą routingu.

Jeśli martwisz się, że nie masz kontroli nad routerem, nie przejmuj się. Ta domyślna konfiguracja świetnie sprawdza się w przypadku szerokiej gamy projektów, pod warunkiem, że są one dobrze ustrukturyzowane. Istnieją jednak przypadki, w których może być konieczne dodanie większej liczby tras do routera, niż Nuxt automatycznie generuje dla Ciebie lub ich restrukturyzację. Nuxt zapewnia sposób dostosowywania instancji routera w konfiguracji, umożliwiając dodawanie nowych tras i dostosowywanie generowanych tras. Możesz także edytować podstawową funkcjonalność instancji routera, w tym dodatkowe opcje dodane przez Nuxt. Jeśli więc napotkasz skrajny przypadek, nadal masz elastyczność w znalezieniu odpowiedniego rozwiązania.

Sklep

Nuxt może zbudować Twój sklep Vuex w oparciu o strukturę katalogu sklepu, podobną do katalogu Pages. Jeśli nie potrzebujesz sklepu, po prostu usuń katalog. W sklepie dostępne są dwa tryby, Klasyczny i Moduły.

Classic wymaga posiadania index.js w katalogu sklepu. Tam musisz wyeksportować funkcję, która zwraca instancję Vuex:

 import Vuex from 'vuex' const createStore = () => { return new Vuex.Store({ state: ..., mutations: ..., actions: ... }) } export default createStore

Pozwala to na stworzenie sklepu tak, jak chcesz, podobnie jak używanie Vuex w normalnym projekcie Vue.

Tryb modułów wymaga również utworzenia index.js w katalogu sklepu. Jednak ten plik wymaga tylko wyeksportowania stanu/mutacji/akcji root dla Twojego sklepu Vuex. Poniższy przykład określa pusty stan główny:

 export const state = () => ({})

Następnie każdy plik w katalogu sklepu zostanie dodany do sklepu we własnej przestrzeni nazw lub module. Na przykład stwórzmy miejsce do przechowywania aktualnego produktu. Jeśli utworzymy plik o nazwie product.js w katalogu sklepu, to sekcja sklepu z przestrzenią nazw będzie dostępna pod adresem $store.product . Oto prosty przykład tego, jak ten plik może wyglądać:

 export const state = () => ({ _id: 0, title: 'Unknown', price: 0 }) export const actions = { load ({ commit }) { setTimeout( commit, 1000, 'update', { _id: 1, title: 'Product', price: 99.99 } ) } } export const mutations = { update (state, product) { Object.assign(state, product) } }

setTimeout w akcji load symuluje pewnego rodzaju wywołanie API, które zaktualizuje sklep z odpowiedzią; w tym przypadku zajmuje to jedną sekundę. Teraz użyjmy go na stronie products/view :

 <template> <div> <h1>View Product {{ product._id }}</h1> <p>{{ product.title }}</p> <p>Price: {{ product.price }}</p> </div> </template> <script> import { mapState } from 'vuex' export default { created () { this.$store.dispatch('product/load') }, computed: { ...mapState(['product']) } } </script>

Kilka rzeczy do zapamiętania: tutaj wywołujemy nasz fałszywy interfejs API podczas tworzenia komponentu. Widać, że wysyłana przez nas akcja product/load ma przestrzeń nazw w sekcji Product. To wyjaśnia dokładnie, z jaką sekcją sklepu mamy do czynienia. Następnie, mapując stan na lokalnie obliczoną właściwość, możemy łatwo użyć jej w naszym szablonie.

Wystąpił problem: widzimy oryginalny stan przez sekundę podczas działania API. Później użyjemy rozwiązania dostarczonego przez Nuxt, aby to naprawić (znanego jako fetch ).

Aby to jeszcze raz podkreślić, nigdy nie musieliśmy npm install vuex , ponieważ jest on już zawarty w pakiecie Nuxt. Kiedy dodajesz plik index.js do katalogu sklepu, wszystkie te metody są automatycznie otwierane.

To są dwa główne wyjaśnione katalogi; reszta jest znacznie prostsza.

składniki

Katalog Components ma zawierać komponenty wielokrotnego użytku, takie jak pasek nawigacyjny, galeria obrazów, paginacja, tabele danych itp. Ponieważ komponenty w katalogu Pages są konwertowane na trasy, potrzebujesz innego miejsca do przechowywania tego typu komponentów. Te komponenty są dostępne na stronach lub innych komponentach po ich zaimportowaniu:

 import ComponentName from ~/components/ComponentName.vue

Aktywa

Zawiera nieskompilowane zasoby i ma więcej wspólnego z ładowaniem i przetwarzaniem plików przez pakiet Webpack niż z działaniem Nuxta. Jeśli jesteś zainteresowany, sugeruję zapoznanie się z przewodnikiem w Readme.

Statyczny

Zawiera statyczne pliki, które są zmapowane do katalogu głównego Twojej witryny. Na przykład umieszczenie obrazu o nazwie logo.png w tym katalogu sprawi, że będzie on dostępny w /logo.png . Jest to dobre dla metaplików, takich jak robots.txt, favicon.ico i innych plików, których potrzebujesz.

Układy

Zwykle w projekcie Vue masz jakiś komponent główny, zwykle nazywany App.vue . Tutaj możesz skonfigurować (zwykle statyczny) układ aplikacji, który może zawierać pasek nawigacyjny, stopkę, a następnie obszar zawartości dla routera vue. default układ robi dokładnie to i jest dostępny w folderze układów. Początkowo wszystko, co ma, to div z <nuxt /> (który jest odpowiednikiem <router-view /> ), ale można go dowolnie stylizować. Na przykład dodałem prosty pasek nawigacyjny do przykładowego projektu do nawigacji po różnych stronach demonstracyjnych.

Układ można zastosować do wielu stron.

Możesz chcieć mieć inny układ dla określonej sekcji swojej aplikacji. Może masz jakiś CMS lub panel administracyjny, który wygląda inaczej. Aby rozwiązać ten problem, utwórz nowy układ w katalogu Układy. Jako przykład stwórzmy układ admin-layout.vue , który ma tylko dodatkowy tag nagłówka i nie ma paska nawigacyjnego:

 <template> <div> <h1>Admin Layout</h1> <nuxt /> </div> </template>

Następnie możemy utworzyć stronę admin.vue w katalogu Pages i użyć właściwości dostarczonej przez Nuxt o nazwie layout , aby określić nazwę (jako ciąg) układu, którego chcemy użyć dla tego komponentu:

 <template> <h1>Admin Page</h1> </template> <script> export default { layout: 'admin-layout' } </script>

To wszystko. Komponenty strony będą używać default układu, chyba że zostanie określony, ale po przejściu do /admin , używa on teraz układu admin-layout.vue . Oczywiście ten układ może być udostępniany na kilku ekranach administracyjnych, jeśli chcesz. Jedyną ważną rzeczą do zapamiętania jest to, że układy muszą zawierać element <nuxt /> .

Na koniec należy zwrócić uwagę na układy. Być może zauważyłeś podczas eksperymentów, że jeśli wpiszesz nieprawidłowy adres URL, zostanie wyświetlona strona błędu. Ta strona błędu jest w rzeczywistości innym układem. Nuxt ma własny układ błędów (tutaj kod źródłowy), ale jeśli chcesz go edytować, po prostu utwórz układ error.vue , który będzie używany zamiast tego. Zastrzeżeniem jest to, że układ błędu nie może zawierać elementu <nuxt /> . Będziesz mieć również dostęp do obiektu error w komponencie z kilkoma podstawowymi informacjami do wyświetlenia. (Jest to wydrukowane w terminalu z uruchomionym Nuxtem, jeśli chcesz to sprawdzić.)

Oprogramowanie pośredniczące

Oprogramowanie pośredniczące to funkcje, które można wykonać przed renderowaniem strony lub układu. Istnieje wiele powodów, dla których możesz to zrobić. Ochrona trasy to popularne zastosowanie, w którym można sprawdzić w sklepie Vuex poprawny login lub sprawdzić poprawność niektórych parametrów (zamiast używać metody validate w samym komponencie). Jeden projekt, nad którym pracowałem, ostatnio wykorzystywał oprogramowanie pośredniczące do generowania dynamicznych okruszków na podstawie trasy i parametrów.

Te funkcje mogą być asynchroniczne; po prostu bądź ostrożny, ponieważ nic nie zostanie pokazane użytkownikowi, dopóki oprogramowanie pośredniczące nie zostanie rozwiązane. Mają też dostęp do kontekstu Nuxta, który wyjaśnię później.

Wtyczki

Ten katalog umożliwia zarejestrowanie wtyczek Vue przed utworzeniem aplikacji. Dzięki temu wtyczka może być udostępniana w całej aplikacji w instancji Vue i być dostępna w dowolnym komponencie.

Większość głównych wtyczek ma wersję Nuxt, którą można łatwo zarejestrować w instancji Vue, postępując zgodnie z ich dokumentacją. Będą jednak okoliczności, w których będziesz rozwijać wtyczkę lub będziesz musiał dostosować w tym celu istniejącą wtyczkę. Przykład, który pożyczam z dokumentów, pokazuje, jak to zrobić dla vue-notifications . Najpierw musimy zainstalować pakiet:

 npm install vue-notifications --save

Następnie utwórz plik w katalogu wtyczek o nazwie vue-notifications.js i dołącz następujące elementy:

 import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)

Bardzo podobny do tego, w jaki można zarejestrować wtyczkę w normalnym środowisku Vue. Następnie edytuj plik nuxt.config.js w katalogu głównym projektu i dodaj następujący wpis do obiektu module.exports:

 plugins: ['~/plugins/vue-notifications']

Otóż ​​to. Teraz możesz używać vue-notifications w całej aplikacji. Przykładem tego jest /plugin w przykładowym projekcie.

Na tym kończy się przegląd struktury katalogów. Może się wydawać, że trzeba się wiele nauczyć, ale jeśli tworzysz aplikację Vue, już konfigurujesz ten sam rodzaj logiki. Nuxt pomaga wyabstrahować konfigurację i pomóc Ci skoncentrować się na budowaniu.

Nuxt nie tylko pomaga w rozwoju. Doładowuje twoje komponenty, zapewniając dodatkową funkcjonalność.

Doładowane komponenty Nuxt

Kiedy po raz pierwszy zacząłem badać Nuxt, czytałem o tym, jak komponenty Page są doładowywane . Brzmiało to świetnie, ale nie od razu było wiadomo, co to właściwie oznacza i jakie przynosi korzyści.

Oznacza to, że do wszystkich składników Page są dołączone dodatkowe metody, które Nuxt może wykorzystać w celu zapewnienia dodatkowej funkcjonalności. W rzeczywistości widzieliśmy już jeden z nich wcześniej, kiedy używaliśmy metody validate do sprawdzania parametrów i przekierowywania użytkownika, jeśli są nieprawidłowe.

Dwie główne metody używane w projekcie Nuxt to metody asyncData i fetch . Obie są bardzo podobne w koncepcji, są uruchamiane asynchronicznie przed wygenerowaniem komponentu i mogą być używane do wypełniania danych komponentu i sklepu. Umożliwiają również pełne renderowanie strony na serwerze przed wysłaniem jej do klienta, nawet gdy musimy czekać na wywołanie jakiejś bazy danych lub API.

Jaka jest różnica między asyncData a fetch ?

  • asyncData służy do wypełniania danych składnika Page. Gdy zwracasz obiekt, jest on scalany z danymi wyjściowymi data przed renderowaniem.
  • fetch służy do zapełniania sklepu Vuex. Jeśli zwrócisz obietnicę, Nuxt poczeka, aż zostanie rozwiązana przed renderowaniem.

Więc wykorzystajmy je dobrze. Pamiętasz, że wcześniej na stronie /products/view mieliśmy problem z wyświetlaniem początkowego stanu sklepu podczas wykonywania fałszywego wywołania API? Jednym ze sposobów rozwiązania tego problemu jest przechowywanie wartości logicznej w komponencie lub w Sklepie, na przykład loading = true a następnie wyświetlanie komponentu ładującego po zakończeniu wywołania API. Następnie ustawimy loading = false i wyświetlimy dane.

Zamiast tego fetch do wypełnienia Sklepu przed renderowaniem. Na nowej stronie o nazwie /products/view-async created metodę na fetch ; to powinno działać, prawda?

 export default { fetch () { // Unfortunately the below line throws an error // because 'this.$store' is undefined... this.$store.dispatch('product/load') }, computed: {...} }

Oto haczyk: te „doładowane” metody są uruchamiane przed utworzeniem komponentu, więc this wskazuje to komponentu i nie można uzyskać do niego dostępu. Jak więc uzyskać dostęp do Sklepu tutaj?

Kontekstowe API

Oczywiście istnieje rozwiązanie. We wszystkich metodach Nuxta otrzymujesz argument (zwykle pierwszy) zawierający niezwykle użyteczny obiekt zwany Kontekstem. Jest to wszystko, do czego będziesz potrzebować odniesienia w całej aplikacji, co oznacza, że ​​nie musimy czekać, aż Vue utworzy najpierw te odniesienia w komponencie.

Gorąco polecam zapoznanie się z dokumentacją kontekstową, aby zobaczyć, co jest dostępne. Niektóre przydatne to app , w którym można uzyskać dostęp do wszystkich wtyczek , redirect , którego można użyć do zmiany tras, error do wyświetlenia strony błędu oraz niektóre oczywiste, takie jak route , query i store .

Tak więc, aby uzyskać dostęp do Sklepu, możemy zdestrukturyzować Kontekst i wyodrębnić z niego Sklep. Musimy również upewnić się, że zwrócimy obietnicę, aby Nuxt mógł poczekać na jej rozwiązanie przed wyrenderowaniem komponentu, więc musimy również wprowadzić niewielką zmianę w naszej akcji Sklepu.

 // Component export default { fetch ({ store }) { return store.dispatch('product/load') }, computed: {...} } // Store Action load ({ commit }) { return new Promise(resolve => { setTimeout(() => { commit('update', { _id: 1, title: 'Product', price: 99.99 }) resolve() }, 1000) }) }

Możesz użyć async/await lub innych metod w zależności od stylu kodowania, ale koncepcja jest taka sama — mówimy Nuxt, aby upewnił się, że wywołanie API zakończy się, a Store zostanie zaktualizowany o wynik przed próbą renderowania komponentu. Jeśli spróbujesz przejść do /products/view-async , nie zobaczysz flasha treści, gdy produkt jest w stanie początkowym.

Możesz sobie wyobrazić, jak przydatne może być to w dowolnej aplikacji Vue, nawet bez SSR. Kontekst jest również dostępny dla wszystkich programów pośredniczących , a także dla innych metod Nuxt, takich jak NuxtServerInit , która jest specjalną akcją sklepu uruchamianą przed zainicjowaniem sklepu (przykład tego znajduje się w następnej sekcji)

Uwagi dotyczące korzystania z protokołu SSR

Jestem pewien, że wielu (w tym ja), którzy zaczynają używać technologii takiej jak Nuxt, traktując ją jak każdy inny projekt Vue, w końcu uderza w ścianę, na której coś, o czym wiemy, że normalnie działałoby, wydaje się niemożliwe w Nuxt. W miarę udokumentowania większej liczby tych zastrzeżeń będzie łatwiej je przezwyciężyć, ale najważniejszą rzeczą do rozważenia przy rozpoczynaniu debugowania jest to, że klient i serwer to dwie oddzielne jednostki.

Kiedy początkowo uzyskujesz dostęp do strony, do Nuxt wysyłane jest żądanie, serwer buduje jak najwięcej tej strony i reszty aplikacji, a następnie wysyła je do Ciebie. Następnie na kliencie spoczywa odpowiedzialność za kontynuowanie nawigacji i ładowanie porcji zgodnie z potrzebami.

Chcemy, aby serwer najpierw robił jak najwięcej, ale czasami nie ma dostępu do potrzebnych informacji, co powoduje, że praca jest wykonywana po stronie klienta. Albo gorzej, gdy ostateczna zawartość prezentowana przez klienta różni się od tego, czego oczekiwał serwer, klient otrzymuje polecenie odbudowania jej od zera. Jest to duża wskazówka, że ​​coś jest nie tak z logiką aplikacji. Na szczęście w konsoli przeglądarki (w trybie deweloperskim) zostanie wygenerowany błąd, jeśli to się stanie.

Weźmy przykład rozwiązania typowego problemu, jakim jest zarządzanie sesjami. Wyobraź sobie, że masz aplikację Vue, w której możesz zalogować się na konto, a Twoja sesja jest przechowywana za pomocą tokena (na przykład JWT), który decydujesz się zachować w localStorage . Kiedy początkowo uzyskujesz dostęp do witryny, chcesz uwierzytelnić ten token w interfejsie API, który zwraca niektóre podstawowe informacje o użytkowniku, jeśli są prawidłowe, i umieszcza te informacje w Sklepie.

Po przeczytaniu dokumentacji Nuxta zobaczysz, że istnieje przydatna metoda o nazwie NuxtServerInit , która umożliwia asynchroniczne zapełnienie Sklepu raz przy pierwszym załadowaniu. To brzmi idealnie! Tworzysz więc swój moduł użytkownika w Sklepie i dodajesz odpowiednią akcję w index.js w katalogu Sklepu:

 export const actions = { nuxtServerInit ({ dispatch }) { // localStorage should work, right? const token = localStorage.getItem('token') if (token) return dispatch('user/load', token) } }

Gdy odświeżysz stronę, pojawi się błąd, localStorage is not defined . Myśląc o tym, gdzie to się dzieje, ma to sens. Ta metoda jest uruchamiana na serwerze, nie ma pojęcia, co jest przechowywane w localStorage na kliencie; w rzeczywistości nawet nie wie, czym jest „localStorage”! Więc to nie jest opcja.

Serwer próbuje wykonać localStorage.getItem('token'), ale zgłasza błąd, a następnie podpis poniżej wyjaśniający problem.

Więc jakie jest rozwiązanie? Właściwie jest ich kilka. Możesz zamiast tego nakłonić klienta do zainicjowania Sklepu, ale w końcu straci korzyści z SSR, ponieważ klient wykona całą pracę. Możesz skonfigurować sesje na serwerze, a następnie użyć ich do uwierzytelnienia użytkownika, ale to kolejna warstwa do skonfigurowania. To, co najbardziej przypomina metodę localStorage , to używanie plików cookie.

Nuxt ma dostęp do plików cookie, ponieważ są one wysyłane z żądaniem od klienta do serwera. Podobnie jak w przypadku innych metod Nuxt, nuxtServerInit ma dostęp do Kontekstu, tym razem jako drugi argument, ponieważ pierwszy jest zarezerwowany dla sklepu. W Context możemy uzyskać dostęp do obiektu req , który przechowuje wszystkie nagłówki i inne informacje z żądania klienta. (Będzie to szczególnie znajome, jeśli używałeś Node.js.)

Więc po zapisaniu tokena w pliku cookie (zwanym w tym przypadku „tokenem”), uzyskajmy do niego dostęp na serwerze.

 import Cookie from 'cookie' export const actions = { nuxtServerInit ({ dispatch }, { req }) { const cookies = Cookie.parse(req.headers.cookie || '') const token = cookies['token'] || '' if (token) return dispatch('user/load', token) } }

Proste rozwiązanie, ale może nie być od razu oczywiste. Nauka myślenia o tym, gdzie dzieją się określone działania (klient, serwer lub oba) i do czego mają dostęp, zajmuje trochę czasu, ale korzyści są tego warte.

Zastosowanie

Wdrożenie za pomocą Nuxta jest niezwykle proste. Korzystając z tej samej bazy kodu, możesz utworzyć aplikację SSR, aplikację jednostronicową lub stronę statyczną.

Aplikacja renderowana po stronie serwera (aplikacja SSR)

To jest prawdopodobnie cel, do którego dążyłeś podczas korzystania z Nuxta. Podstawową koncepcją wdrożenia tutaj jest uruchomienie procesu build na dowolnej wybranej platformie i ustawienie kilku konfiguracji. Użyję przykładu Heroku z dokumentacji:

Najpierw skonfiguruj skrypty dla Heroku w package.json :

 "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "heroku-postbuild": "npm run build" }

Następnie skonfiguruj środowisko Heroku za pomocą heroku-cli (instrukcje konfiguracji tutaj:

 # set Heroku variables heroku config:set NPM_CONFIG_PRODUCTION=false heroku config:set HOST=0.0.0.0 heroku config:set NODE_ENV=production # deploy git push heroku master

Otóż ​​to. Teraz Twoja aplikacja SSR Vue jest gotowa na żywo, aby świat mógł ją zobaczyć. Inne platformy mają różne konfiguracje, ale proces jest podobny. Obecnie wymienione oficjalne metody wdrażania to:

  • Ale już
  • Dokku (cyfrowy ocean)
  • Nginx

Aplikacja jednostronicowa (SPA)

Jeśli chcesz skorzystać z niektórych dodatkowych funkcji oferowanych przez Nuxt, ale uniknąć prób renderowania stron przez serwer, możesz zamiast tego wdrożyć jako SPA.

Najpierw najlepiej przetestować aplikację bez SSR, ponieważ domyślnie npm run dev działa z włączonym SSR. Aby to zmienić, edytuj plik nuxt.config.js i dodaj następującą opcję:

 mode: 'spa',

Teraz, gdy uruchomisz npm run dev , SSR zostanie wyłączony, a aplikacja będzie działać jako SPA do przetestowania. To ustawienie zapewnia również, że żadne przyszłe kompilacje nie będą zawierały SSR.

Jeśli wszystko wygląda dobrze, wdrożenie przebiega dokładnie tak samo, jak w przypadku aplikacji SSR. Pamiętaj tylko, że musisz najpierw ustawić mode: 'spa' , aby proces kompilacji wiedział, że chcesz SPA.

Strony statyczne

Jeśli nie chcesz w ogóle zajmować się serwerem, a zamiast tego chcesz generować strony do użytku ze statycznymi usługami hostingowymi, takimi jak Surge lub Netlify, jest to opcja do wyboru. Pamiętaj tylko, że bez serwera nie będziesz w stanie uzyskać dostępu do req i res w kontekście, więc jeśli twój kod na tym polega, pamiętaj, aby to uwzględnić. Na przykład podczas generowania przykładowego projektu funkcja nuxtServerInit zgłasza błąd, ponieważ próbuje pobrać token z plików cookie w nagłówkach żądania. W tym projekcie nie ma to znaczenia, ponieważ te dane nie są nigdzie używane, ale w prawdziwej aplikacji musiałby istnieć alternatywny sposób dostępu do tych danych.

Po uporządkowaniu wdrożenie jest łatwe. Jedną z rzeczy, które prawdopodobnie będziesz musiał najpierw zmienić, jest dodanie opcji, aby polecenie nuxt generate również tworzyło plik awaryjny. Ten plik poprosi usługę hostingową, aby pozwoliła Nuxtowi zająć się routingiem, a nie usługą hostingową, powodując błąd 404. Aby to zrobić, dodaj następujący wiersz do nuxt.config.js :

 generate: { fallback: true },

Oto przykład korzystania z Netlify, którego obecnie nie ma w dokumentacji Nuxt. Pamiętaj tylko, że jeśli używasz netlify-cli po raz pierwszy, zostaniesz poproszony o uwierzytelnienie:

 # install netlify-cli globally npm install netlify-cli -g # generate the application (outputs to dist/ folder) npm run generate # deploy netlify deploy dist

To takie proste! Jak wspomniano na początku artykułu, jest tutaj wersja tego projektu. Poniżej znajduje się również oficjalna dokumentacja dotycząca wdrażania następujących usług:

  • Wzrost
  • Strony GitHub

Ucz się więcej

Nuxt aktualizuje się szybko, a to tylko niewielki wybór funkcji, które oferuje. Mam nadzieję, że ten artykuł zachęci Cię do wypróbowania go i sprawdzenia, czy może pomóc poprawić możliwości Twoich aplikacji Vue, umożliwiając szybszy rozwój i korzystanie z jego potężnych funkcji.

Jeśli szukasz więcej informacji, nie szukaj dalej niż oficjalne linki Nuxt:

  • Dokumentacja
  • Plac zabaw
  • GitHub
  • FAQ

Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.